summaryrefslogtreecommitdiffstats
path: root/serial_over_dp.c
diff options
context:
space:
mode:
Diffstat (limited to 'serial_over_dp.c')
-rw-r--r--serial_over_dp.c815
1 files changed, 815 insertions, 0 deletions
diff --git a/serial_over_dp.c b/serial_over_dp.c
new file mode 100644
index 0000000..20d55d9
--- /dev/null
+++ b/serial_over_dp.c
@@ -0,0 +1,815 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/io.h>
+
+/*
+ * Linux won't let us access the MMIO bars from userland, becasue of a
+ * conflict in the PAT mappings. However Intel added a way of writing
+ * to the MMIO region using only IO port writes for their 16 bit video
+ * BIOS. So we use that.
+ *
+ * If you're in a kernel, you can write directly, and define this
+ *
+ */
+
+#undef USE_MMIO
+
+/* PCI registers */
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_DEVICE_ID 0x02
+#define PCI_CLASS_DEVICE 0x0a
+
+#define PCI_BASE_ADDRESS(i) (0x10 + (i<< 2))
+#define PCI_BASE_ADDRESS_0 PCI_BASE_ADDRESS(0)
+
+#define PCI_NR_BASE_ADDRESS 6
+
+#define PCI_BASE_ADDRESS_SPACE_IO (0x01UL)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x07UL)
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDRESS_TYPE_MASK (0x06UL)
+#define PCI_BASE_ADDRESS_TYPE_64BIT (0x04UL)
+#define PCI_BASE_ADDRESS_PREFETCHABLE (0x08UL)
+
+/* Intel GPU registers */
+#define IGFX_PCH_OFFSET 0xc0000
+
+#define IGFX_GPIOA 0x05010
+#define IGFX_GPIO(port) ((IGFX_GPIOA) + ((port) << 2))
+#define IGFX_NUM_PORTS 6
+
+#define IGFX_GMBUS0 0x05100
+#define IGFX_GMBUS4 0x05110
+
+/*
+ * These look like independent bits, but on the other
+ * side it's implemented by a uC. The uC programmers
+ * have implemented it as an RPC-a-like interface so
+ * so it gets confused if you change more than one
+ * set of pins at a time.
+ */
+
+#define GPIO_CLOCK_DIR_MASK (1 << 0)
+#define GPIO_CLOCK_DIR_IN (0 << 1)
+#define GPIO_CLOCK_DIR_OUT (1 << 1)
+#define GPIO_CLOCK_VAL_MASK (1 << 2)
+#define GPIO_CLOCK_VAL_OUT (1 << 3)
+#define GPIO_CLOCK_VAL_IN (1 << 4)
+#define GPIO_CLOCK_PULLUP_DISABLE (1 << 5)
+#define GPIO_DATA_DIR_MASK (1 << 8)
+#define GPIO_DATA_DIR_IN (0 << 9)
+#define GPIO_DATA_DIR_OUT (1 << 9)
+#define GPIO_DATA_VAL_MASK (1 << 10)
+#define GPIO_DATA_VAL_OUT (1 << 11)
+#define GPIO_DATA_VAL_IN (1 << 12)
+#define GPIO_DATA_PULLUP_DISABLE (1 << 13)
+
+/* I2C addresses (left shifted by one) */
+
+#define I2C_EEPROM_ADDRESS 0xA0
+#define I2C_SC16IS7XX_ADDRESS 0x90
+
+#define I2C_READ 1
+#define I2C_WRITE 0
+
+/* 16550 registers */
+
+#define BAUD_BASE 1843200
+
+#define RXR ((0))
+#define TXR ((0))
+#define DLLR ((0))
+
+#define IER ((1))
+#define DLHR ((1))
+
+#define IIR ((2))
+#define FCR ((2))
+
+#define LCR ((3))
+#define LCR_WS0 (1<<0)
+#define LCR_WS1 (1<<1)
+#define LCR_NSTOP (1<<2)
+#define LCR_PARITY_ENABLE (1<<3)
+#define LCR_EVEN_PARITY (1<<4)
+#define LCR_STUCK_PARITY (1<<5)
+#define LCR_BREAK (1<<6)
+#define LCR_DLAB (1<<7)
+
+#define MCR ((4))
+#define MCR_DTR (1<<0)
+#define MCR_RTS (1<<1)
+#define MCR_AUX1 (1<<2)
+#define MCR_AUX2 (1<<3)
+#define MCR_LOOP (1<<4)
+#define MCR_AUTOFLOW (1<<5)
+
+#define LSR ((5))
+#define LSR_DA (1<<0)
+#define LSR_OR (1<<1)
+#define LSR_PE (1<<2)
+#define LSR_FE (1<<3)
+#define LSR_BRK (1<<4)
+#define LSR_THRE (1<<5)
+#define LSR_THREI (1<<6)
+#define LSR_FIFOE (1<<7)
+
+#define MSR ((6))
+
+#define TXR_NOINC ((TXR) | (1UL << 4))
+
+#define BAUD_RATE 115200
+
+
+/* intrinsics */
+
+static inline void
+out_8 (uint16_t port, uint8_t value)
+{
+ asm volatile ("outb %b0,%w1"::"a" (value), "Nd" (port));
+}
+
+
+static inline uint8_t
+in_8 (uint16_t port)
+{
+ uint8_t ret;
+ asm volatile ("inb %w1,%b0":"=a" (ret):"Nd" (port));
+ return ret;
+}
+
+
+static inline uint16_t
+in_16 (uint16_t port)
+{
+ uint16_t ret;
+ asm volatile ("inw %w1,%w0":"=a" (ret):"Nd" (port));
+ return ret;
+}
+
+static inline void
+out_16 (uint16_t port, uint16_t value)
+{
+ asm volatile ("outw %w0,%w1"::"a" (value), "Nd" (port));
+}
+
+static inline void
+out_32 (uint16_t port, uint32_t value)
+{
+ asm volatile ("outl %d0,%w1"::"a" (value), "Nd" (port));
+}
+
+static inline uint32_t
+in_32 (uint16_t port)
+{
+ uint32_t ret;
+ asm volatile ("inl %w1,%d0":"=a" (ret):"Nd" (port));
+ return ret;
+}
+
+static inline void
+io_delay (void)
+{
+ out_8 (0x80, 0x0);
+}
+
+static inline void
+write_32 (volatile void *addr, uint32_t val)
+{
+ asm volatile ("mov" "l" " %0,%1"::"r" (val),
+ "m" (*(volatile uint32_t *) addr):"memory");
+}
+
+static inline uint32_t
+read_32 (const volatile void *addr)
+{
+ uint32_t ret;
+ asm volatile ("mov" "l"
+ " %1,%0":"=r" (ret):"m" (*(volatile uint32_t *)
+ addr):"memory");
+ return ret;
+}
+
+
+/* PCI */
+
+static uint16_t
+pci_conf1_read_16 (int bus, int slot, int fn, int off)
+{
+ int addr = 0xcfc + (off & 3);
+ out_32 (0x0cf8,
+ 0x80000000 | (bus << 16) | (slot << 11) | (fn << 8) | (off & ~3));
+ return in_16 (addr);
+}
+
+static uint32_t
+pci_conf1_read_32 (int bus, int slot, int fn, int off)
+{
+ int addr = 0xcfc + (off & 3);
+ out_32 (0x0cf8,
+ 0x80000000 | (bus << 16) | (slot << 11) | (fn << 8) | (off & ~3));
+ return in_32 (addr);
+}
+
+
+/* IGFX */
+
+static uint64_t igfx_mmio_base;
+
+static int
+igfx_set_mmio_base (void)
+{
+ uint16_t vid, class;
+ unsigned b = 0, d = 2, f = 0;
+ uint64_t bar;
+ unsigned i;
+
+ igfx_mmio_base = 0;
+
+ vid = pci_conf1_read_32 (b, d, f, PCI_VENDOR_ID);
+
+ if (vid != 0x8086)
+ return 0;
+
+ class = pci_conf1_read_16 (b, d, f, PCI_CLASS_DEVICE);
+
+ if (class != 0x300)
+ return -1;
+
+ for (i = 0; i < PCI_NR_BASE_ADDRESS; i++)
+ {
+ bar = pci_conf1_read_32 (b, d, f, PCI_BASE_ADDRESS (i));
+
+ if (!bar)
+ continue;
+
+#ifdef USE_MMIO
+
+ if (!bar)
+ continue;
+
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO)
+ continue;
+
+ if ((bar & PCI_BASE_ADDRESS_TYPE_MASK) == PCI_BASE_ADDRESS_TYPE_64BIT)
+ {
+ i++;
+ bar |=
+ ((uint64_t) pci_conf1_read_32 (b, d, f, PCI_BASE_ADDRESS (i))) <<
+ 32;
+ }
+
+ if (bar & PCI_BASE_ADDRESS_PREFETCHABLE)
+ continue;
+
+ igfx_mmio_base = bar & ~0xfULL;
+#else
+ if (!(bar & PCI_BASE_ADDRESS_SPACE_IO))
+ continue;
+
+ igfx_mmio_base = bar & ~0xfULL;
+#endif
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void
+igfx_write_32 (uint32_t reg, uint32_t val)
+{
+#ifdef USE_MMIO
+ uint32_t *p = (uint32_t *) (igfx_mmio_base + reg);
+#endif
+
+ if (!igfx_mmio_base)
+ return;
+
+#ifdef USE_MMIO
+ write_32 (p, val);
+#else
+ out_32 (igfx_mmio_base, reg);
+ out_32 (igfx_mmio_base + 4, val);
+#endif
+}
+
+
+static uint32_t
+igfx_read_32 (uint32_t reg)
+{
+#ifdef USE_MMIO
+ uint32_t *p = (uint32_t *) (igfx_mmio_base + reg);
+#endif
+
+ if (!igfx_mmio_base)
+ return ~0;
+
+#ifdef USE_MMIO
+ return read_32 (p);
+#else
+ out_32 (igfx_mmio_base, reg);
+ return in_32 (igfx_mmio_base + 4);
+#endif
+}
+
+/* IGFX GPIO */
+
+static uint64_t igfx_gpio_ctl;
+
+static int
+igfx_set_gpio_ctl (unsigned port)
+{
+ igfx_gpio_ctl = 0;
+
+ if (igfx_set_mmio_base ())
+ return -1;
+
+ igfx_gpio_ctl = IGFX_PCH_OFFSET + IGFX_GPIO (port);
+
+ return 0;
+}
+
+static inline void
+igfx_write_gpio_ctl (uint32_t v)
+{
+ igfx_write_32 (igfx_gpio_ctl, v);
+ igfx_read_32 (igfx_gpio_ctl);
+}
+
+
+static inline uint32_t
+igfx_read_gpio_ctl (void)
+{
+ igfx_read_32 (igfx_gpio_ctl);
+ return igfx_read_32 (igfx_gpio_ctl);
+}
+
+/* I2C bits */
+
+static void
+i2c_set_scl (int clk)
+{
+ igfx_write_gpio_ctl (clk ? (GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK)
+ : (GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+ GPIO_CLOCK_VAL_MASK));
+}
+
+
+static void
+i2c_set_sda (int dat)
+{
+ igfx_write_gpio_ctl (dat ? (GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK)
+ : (GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+ GPIO_DATA_VAL_MASK));
+}
+
+static int
+i2c_get_scl (void)
+{
+ return ! !(igfx_read_gpio_ctl () & GPIO_CLOCK_VAL_IN);
+}
+
+
+static int
+i2c_get_sda (void)
+{
+ return ! !(igfx_read_gpio_ctl () & GPIO_DATA_VAL_IN);
+}
+
+static void
+i2c_bit_wait (void)
+{
+ io_delay ();
+}
+
+static void
+i2c_wait_scl (void)
+{
+ static int no_wait_scl = 0;
+ unsigned n = 1000;
+
+ if (i2c_get_scl ())
+ {
+ no_wait_scl = 0;
+ return;
+ }
+
+ if (no_wait_scl == 1)
+ return;
+
+ while (!i2c_get_scl ())
+ {
+ if (!(n--))
+ {
+ no_wait_scl = 1;
+ return;
+ }
+
+ i2c_bit_wait ();
+ }
+}
+
+
+/* I2C protocol */
+
+static void
+i2c_start (void)
+{
+ i2c_set_scl (1);
+ i2c_set_sda (1);
+ i2c_bit_wait ();
+
+ i2c_set_sda (0);
+ i2c_bit_wait ();
+
+ i2c_set_scl (0);
+ i2c_bit_wait ();
+}
+
+static void
+i2c_write_bit (int b)
+{
+ i2c_set_scl (0);
+ i2c_set_sda (b);
+ i2c_bit_wait ();
+
+
+ i2c_set_scl (1);
+ i2c_bit_wait ();
+ i2c_wait_scl ();
+
+ i2c_set_scl (0);
+ i2c_bit_wait ();
+}
+
+/* Send a stop signal to the I2C bus */
+static void
+i2c_stop ()
+{
+
+ i2c_set_sda (0);
+ i2c_set_scl (0);
+ i2c_bit_wait ();
+
+ i2c_set_scl (1);
+ i2c_bit_wait ();
+
+ i2c_set_sda (1);
+ i2c_bit_wait ();
+}
+
+/* Write the specified number of bits to the I2C bus (msb first) */
+static void
+i2c_write_bits (uint32_t d, unsigned n)
+{
+ uint32_t c;
+
+ for (c = 1 << (n - 1); c; c >>= 1)
+ i2c_write_bit (d & c);
+}
+
+static int
+i2c_read_bit (void)
+{
+ int ret;
+
+ i2c_set_sda (1);
+ i2c_set_scl (0);
+ i2c_bit_wait ();
+
+ i2c_set_scl (1);
+ i2c_bit_wait ();
+ i2c_wait_scl ();
+
+ ret = i2c_get_sda ();
+
+ i2c_set_scl (0);
+ i2c_bit_wait ();
+
+ return ret;
+}
+
+
+static uint32_t
+i2c_read_bits (unsigned n)
+{
+ uint32_t ret = 0;
+
+ while (n--)
+ {
+ ret <<= 1;
+ ret |= i2c_read_bit ()? 1 : 0;
+ }
+
+ return ret;
+}
+
+static int
+i2c_read_bytes (uint8_t * buf, size_t n)
+{
+ while (n--)
+ {
+ *(buf++) = i2c_read_bits (8);
+ i2c_write_bit (!n); /*Don't ACK last byte */
+ }
+
+ return 0;
+}
+
+static int
+i2c_write_bytes (uint8_t * buf, size_t n)
+{
+ while (n--)
+ {
+ i2c_write_bits (*(buf++), 8);
+
+ if (i2c_read_bit ()) /*check ACK */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+i2c_read (uint8_t i2c_addr, uint8_t dev_addr, uint8_t * buf, size_t n)
+{
+ int ret = -1;
+
+ i2c_start ();
+ i2c_write_bits (i2c_addr | I2C_WRITE, 8);
+
+ do
+ {
+ if (i2c_read_bit ()) /*check ACK */
+ break;
+
+ i2c_write_bits (dev_addr, 8);
+
+ if (i2c_read_bit ()) /*check ACK */
+ break;
+
+ if (n)
+ {
+ i2c_start ();
+ i2c_write_bits (i2c_addr | I2C_READ, 8);
+
+ if (i2c_read_bit ()) /*check ACK */
+ break;
+
+ if (i2c_read_bytes (buf, n))
+ break;
+ }
+
+
+ ret = 0;
+
+
+ }
+ while (0);
+
+
+ i2c_stop ();
+
+ return ret;
+}
+
+static int
+i2c_write (uint8_t i2c_addr, uint8_t dev_addr, uint8_t * buf, size_t n)
+{
+ int ret = -1;
+
+ i2c_start ();
+ i2c_write_bits (i2c_addr | I2C_WRITE, 8);
+
+ do
+ {
+ if (i2c_read_bit ()) /*check ACK */
+ break;
+
+ i2c_write_bits (dev_addr, 8);
+
+ if (i2c_read_bit ()) /*check ACK */
+ break;
+
+ if (n && i2c_write_bytes (buf, n))
+ break;
+
+ ret = 0;
+
+ }
+ while (0);
+
+
+ i2c_stop ();
+
+ return ret;
+}
+
+static int
+i2c_test_state (int s_sda, int s_scl)
+{
+ int g_sda, g_scl;
+
+ s_sda = ! !s_sda;
+ s_scl = ! !s_scl;
+
+ i2c_set_scl (s_scl);
+ i2c_set_sda (s_sda);
+ i2c_bit_wait ();
+ g_scl = i2c_get_scl ();
+ g_sda = i2c_get_sda ();
+
+ if ((g_scl != s_scl) || (g_sda != s_sda))
+ return 1;
+
+ return 0;
+}
+
+
+static int
+i2c_test_adapter (void)
+{
+ int ret = 0;
+ ret |= i2c_test_state (1, 1);
+ ret |= i2c_test_state (0, 1);
+ ret |= i2c_test_state (0, 0);
+ ret |= i2c_test_state (0, 1);
+ ret |= i2c_test_state (1, 1);
+ return ret;
+}
+
+static int
+i2c_init_adapter (unsigned port)
+{
+
+ if (igfx_set_gpio_ctl (port))
+ return -1;
+
+ if (i2c_test_adapter ())
+ return -1;
+
+ return 0;
+}
+
+/* serial port */
+
+
+static uint8_t
+serial_read_reg (unsigned reg)
+{
+ uint8_t val = 0;
+ i2c_read (I2C_SC16IS7XX_ADDRESS, reg, &val, 1);
+ return val;
+}
+
+
+static void
+serial_write_reg (unsigned reg, uint8_t val)
+{
+ i2c_write (I2C_SC16IS7XX_ADDRESS, reg, &val, 1);
+}
+
+static void
+serial_write_reg_buf (unsigned reg, uint8_t * buf, size_t len)
+{
+ i2c_write (I2C_SC16IS7XX_ADDRESS, reg, buf, len);
+}
+
+
+static int
+serial_probe (void)
+{
+ uint8_t ier, lcr, dlh;
+
+ lcr = serial_read_reg (LCR);
+ serial_write_reg (LCR, lcr | LCR_DLAB);
+ dlh = serial_read_reg (DLHR);
+ serial_write_reg (DLHR, 0x55);
+ serial_write_reg (LCR, lcr);
+
+ ier = serial_read_reg (IER);
+ serial_write_reg (IER, 0x00);
+
+ if (serial_read_reg (IER) != 0x00)
+ return 1;
+
+ serial_write_reg (IER, ier);
+
+ serial_write_reg (LCR, lcr | LCR_DLAB);
+
+ if (serial_read_reg (DLHR) != 0x55)
+ return 1;
+
+ serial_write_reg (DLHR, 0xaa);
+
+ if (serial_read_reg (DLHR) != 0xaa)
+ return 1;
+
+ serial_write_reg (DLHR, dlh);
+ serial_write_reg (LCR, lcr);
+
+ return 0;
+}
+
+
+static void
+serial_set_baud (unsigned baud)
+{
+ uint32_t divisor = (BAUD_BASE >> 4) / baud;
+ uint8_t lcr;
+
+ lcr = serial_read_reg (LCR);
+ serial_write_reg (LCR, lcr | LCR_DLAB);
+
+ serial_write_reg (DLHR, divisor >> 8);
+ serial_write_reg (DLLR, divisor & 0xff);
+
+ serial_write_reg (LCR, lcr);
+}
+
+
+void
+serial_init (void)
+{
+
+ serial_write_reg (FCR, 0x6); /*disable FIFOs and reset */
+ serial_write_reg (IER, 0); /* no interrupts */
+
+ /* Clear all interrupts */
+ serial_read_reg (IIR);
+ serial_read_reg (MSR);
+ serial_read_reg (RXR);
+ serial_read_reg (LSR);
+
+ serial_set_baud (BAUD_RATE);
+
+ serial_write_reg (LCR, LCR_WS0 | LCR_WS1); /*8 bits, 1 stop bit, no parity */
+ serial_write_reg (MCR, MCR_RTS | MCR_DTR); /*rts and dtr on */
+}
+
+
+void
+serial_write (void *buf, size_t len)
+{
+/*
+ * If we were to use a real SC16IS7XX
+ * we'd not be able to use this special
+ * extra non-incrementing register
+ * and we'd also have to check the THRE
+ * bit first
+ */
+
+ serial_write_reg_buf (TXR_NOINC, buf, len);
+}
+
+
+
+/* main */
+
+int
+find_serial (void)
+{
+ unsigned i;
+
+ for (i = 0; i < IGFX_NUM_PORTS; ++i)
+ {
+ if (i2c_init_adapter (i))
+ continue;
+
+ printf ("Found active IGFX I2C bus %d\n", i);
+
+ if (serial_probe ())
+ continue;
+
+ printf ("Serial port found on IGFX I2C bus %d\n", i);
+ return 0;
+
+ }
+ printf ("Serial port not found\n");
+ return -1;
+}
+
+
+/* main */
+
+int
+main (int argc, char *argv[])
+{
+ char msg[] = "The quick brown fox jumps over the lazy dog\r\n";
+
+ iopl (3);
+
+ if (find_serial ())
+ return -1;
+
+ serial_init ();
+
+ serial_write (msg, sizeof (msg));
+
+ return 0;
+}