#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; }