From 809b234c2b9cd5fb3fe3bc6f7611cc30b9fe40c8 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 14 Jun 2017 09:10:58 +0100 Subject: initial commit --- serial_over_dp.c | 815 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 815 insertions(+) create mode 100644 serial_over_dp.c (limited to 'serial_over_dp.c') 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 +#include +#include +#include + +/* + * 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; +} -- cgit v1.2.3