--- b/sys/dev/ichiic/ig4_acpi.c +++ b/sys/dev/ichiic/ig4_acpi.c @@ -85,6 +85,8 @@ ig4iic_acpi_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; + /* All the HIDs matched are Atom SOCs. */ + sc->version = IG4_ATOM; sc->regs_rid = 0; sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regs_rid, RF_ACTIVE); --- b/sys/dev/ichiic/ig4_iic.c +++ b/sys/dev/ichiic/ig4_iic.c @@ -117,8 +117,9 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl) reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET | IG4_INTR_RX_FULL); reg_read(sc, IG4_REG_CLR_INTR); - } else + } else { reg_write(sc, IG4_REG_INTR_MASK, 0); + } reg_write(sc, IG4_REG_I2C_EN, ctl); error = IIC_ETIMEOUT; @@ -134,6 +135,8 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl) else mtx_sleep(sc, &sc->io_lock, 0, "i2cslv", 1); } + + return (error); } @@ -525,23 +528,53 @@ ig4iic_attach(ig4iic_softc_t *sc) mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); sx_init(&sc->call_lock, "IG4 call lock"); + v = reg_read(sc, IG4_REG_DEVIDLE_CTRL); + if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) { + reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED); + reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0); + + reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); + reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); + DELAY(1000); + } + v = reg_read(sc, IG4_REG_COMP_TYPE); v = reg_read(sc, IG4_REG_COMP_PARAM1); v = reg_read(sc, IG4_REG_GENERAL); - if ((v & IG4_GENERAL_SWMODE) == 0) { - v |= IG4_GENERAL_SWMODE; - reg_write(sc, IG4_REG_GENERAL, v); - v = reg_read(sc, IG4_REG_GENERAL); + if (sc->version == IG4_ATOM) { + v = reg_read(sc, IG4_REG_COMP_TYPE); + } + if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { + v = reg_read(sc, IG4_REG_COMP_PARAM1); + v = reg_read(sc, IG4_REG_GENERAL); + /* + * The content of IG4_REG_GENERAL is different for each + * controller version. + */ + if (sc->version == IG4_HASWELL && + (v & IG4_GENERAL_SWMODE) == 0) { + v |= IG4_GENERAL_SWMODE; + reg_write(sc, IG4_REG_GENERAL, v); + v = reg_read(sc, IG4_REG_GENERAL); + } } - v = reg_read(sc, IG4_REG_SW_LTR_VALUE); - v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); + if (sc->version == IG4_HASWELL) { + v = reg_read(sc, IG4_REG_SW_LTR_VALUE); + v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); + } else if (sc->version == IG4_SKYLAKE) { + v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE); + v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE); + } - v = reg_read(sc, IG4_REG_COMP_VER); - if (v != IG4_COMP_VER) { - error = ENXIO; - goto done; + if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { + v = reg_read(sc, IG4_REG_COMP_VER); + if (v != IG4_COMP_VER) { + error = ENXIO; + goto done; + } } + v = reg_read(sc, IG4_REG_SS_SCL_HCNT); v = reg_read(sc, IG4_REG_SS_SCL_LCNT); v = reg_read(sc, IG4_REG_FS_SCL_HCNT); @@ -591,8 +624,10 @@ ig4iic_attach(ig4iic_softc_t *sc) /* * Don't do this, it blows up the PCI config */ - reg_write(sc, IG4_REG_RESETS, IG4_RESETS_ASSERT); - reg_write(sc, IG4_REG_RESETS, IG4_RESETS_DEASSERT); + if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { + reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW); + reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW); + } #endif mtx_lock(&sc->io_lock); @@ -727,14 +762,27 @@ ig4iic_dump(ig4iic_softc_t *sc) REGDUMP(sc, IG4_REG_DMA_RDLR); REGDUMP(sc, IG4_REG_SDA_SETUP); REGDUMP(sc, IG4_REG_ENABLE_STATUS); - REGDUMP(sc, IG4_REG_COMP_PARAM1); - REGDUMP(sc, IG4_REG_COMP_VER); - REGDUMP(sc, IG4_REG_COMP_TYPE); - REGDUMP(sc, IG4_REG_CLK_PARMS); - REGDUMP(sc, IG4_REG_RESETS); - REGDUMP(sc, IG4_REG_GENERAL); - REGDUMP(sc, IG4_REG_SW_LTR_VALUE); - REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE); + if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { + REGDUMP(sc, IG4_REG_COMP_PARAM1); + REGDUMP(sc, IG4_REG_COMP_VER); + } + if (sc->version == IG4_ATOM) { + REGDUMP(sc, IG4_REG_COMP_TYPE); + REGDUMP(sc, IG4_REG_CLK_PARMS); + } + if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { + REGDUMP(sc, IG4_REG_RESETS_HSW); + REGDUMP(sc, IG4_REG_GENERAL); + } else if (sc->version == IG4_SKYLAKE) { + REGDUMP(sc, IG4_REG_RESETS_SKL); + } + if (sc->version == IG4_HASWELL) { + REGDUMP(sc, IG4_REG_SW_LTR_VALUE); + REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE); + } else if (sc->version == IG4_SKYLAKE) { + REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE); + REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE); + } } #undef REGDUMP --- b/sys/dev/ichiic/ig4_pci.c +++ b/sys/dev/ichiic/ig4_pci.c @@ -66,46 +66,62 @@ __FBSDID("$FreeBSD$"); static int ig4iic_pci_detach(device_t dev); -#define PCI_CHIP_LYNXPT_LP_I2C_1 0x9c618086 -#define PCI_CHIP_LYNXPT_LP_I2C_2 0x9c628086 -#define PCI_CHIP_BRASWELL_I2C_1 0x22c18086 -#define PCI_CHIP_BRASWELL_I2C_2 0x22c28086 -#define PCI_CHIP_BRASWELL_I2C_3 0x22c38086 -#define PCI_CHIP_BRASWELL_I2C_5 0x22c58086 -#define PCI_CHIP_BRASWELL_I2C_6 0x22c68086 -#define PCI_CHIP_BRASWELL_I2C_7 0x22c78086 +#ifndef NELEM +#define NELEM(ary) (sizeof(ary) / sizeof((ary)[0])) +#endif + +static struct { + uint32_t id; + char *name; + enum ig4_vers version; +} intel_i2c_ids[] = { + /* Haswell */ + { 0x9c618086, "Intel Lynx Point-LP I2C Controller-1", IG4_HASWELL }, + { 0x9c628086, "Intel Lynx Point-LP I2C Controller-2", IG4_HASWELL }, + + /* Braswell */ + { 0x22c18086, "Intel Braswell Serial I/O I2C Port 1", IG4_HASWELL }, + { 0x22c28086, "Intel Braswell Serial I/O I2C Port 2", IG4_HASWELL }, + { 0x22c38086, "Intel Braswell Serial I/O I2C Port 3", IG4_HASWELL }, + { 0x22c58086, "Intel Braswell Serial I/O I2C Port 4", IG4_HASWELL }, + { 0x22c68086, "Intel Braswell Serial I/O I2C Port 6", IG4_HASWELL }, + { 0x22c78086, "Intel Braswell Serial I/O I2C Port 7", IG4_HASWELL }, + + /* Skylake-U/Y and Kaby Lake-U/Y CPUs */ + { 0x9d608086, "Intel Sunrise Point-LP I2C Controller-0", IG4_SKYLAKE }, + { 0x9d618086, "Intel Sunrise Point-LP I2C Controller-1", IG4_SKYLAKE }, + { 0x9d628086, "Intel Sunrise Point-LP I2C Controller-2", IG4_SKYLAKE }, + { 0x9d638086, "Intel Sunrise Point-LP I2C Controller-3", IG4_SKYLAKE }, + { 0x9d648086, "Intel Sunrise Point-LP I2C Controller-4", IG4_SKYLAKE }, + { 0x9d658086, "Intel Sunrise Point-LP I2C Controller-5", IG4_SKYLAKE }, + + /* Kabylake-H CPUs*/ + { 0xa1608086, "Intel Sunrise Point-H I2C Controller-0", IG4_SKYLAKE }, + { 0xa1618086, "Intel Sunrise Point-H I2C Controller-1", IG4_SKYLAKE }, +}; static int ig4iic_pci_probe(device_t dev) { - switch(pci_get_devid(dev)) { - case PCI_CHIP_LYNXPT_LP_I2C_1: - device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1"); - break; - case PCI_CHIP_LYNXPT_LP_I2C_2: - device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2"); - break; - case PCI_CHIP_BRASWELL_I2C_1: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 1"); - break; - case PCI_CHIP_BRASWELL_I2C_2: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 2"); - break; - case PCI_CHIP_BRASWELL_I2C_3: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 3"); - break; - case PCI_CHIP_BRASWELL_I2C_5: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 5"); - break; - case PCI_CHIP_BRASWELL_I2C_6: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 6"); - break; - case PCI_CHIP_BRASWELL_I2C_7: - device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 7"); - break; - default: + int i; + uint32_t device_id; + const char *name = NULL; + + device_id = pci_get_devid(dev); + for (i = 0; i < NELEM(intel_i2c_ids); i++) { + if (intel_i2c_ids[i].id == device_id) { + name = intel_i2c_ids[i].name; + device_printf(dev, "found %s\n", name); + break; + } + } + + if (name != NULL) { + device_set_desc(dev, name); + } else { return (ENXIO); } + return (BUS_PROBE_DEFAULT); } @@ -113,7 +129,16 @@ static int ig4iic_pci_attach(device_t dev) { ig4iic_softc_t *sc = device_get_softc(dev); - int error; + int error, i; + uint32_t device_id; + + device_id = pci_get_devid(dev); + for (i = 0; i < NELEM(intel_i2c_ids); i++) { + if (intel_i2c_ids[i].id == device_id) { + sc->version = intel_i2c_ids[i].version; + break; + } + } sc->dev = dev; sc->regs_rid = PCIR_BAR(0); @@ -194,8 +219,7 @@ static driver_t ig4iic_pci_driver = { static devclass_t ig4iic_pci_devclass; -DRIVER_MODULE_ORDERED(ig4iic_pci, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0, - SI_ORDER_ANY); +DRIVER_MODULE(ig4iic_pci, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0); MODULE_DEPEND(ig4iic_pci, pci, 1, 1, 1); MODULE_DEPEND(ig4iic_pci, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(ig4iic_pci, 1); --- b/sys/dev/ichiic/ig4_reg.h +++ b/sys/dev/ichiic/ig4_reg.h @@ -78,6 +78,7 @@ #define IG4_REG_CTL 0x0000 /* RW Control Register */ #define IG4_REG_TAR_ADD 0x0004 /* RW Target Address */ +#define IG4_REG_HS_MADDR 0x000C /* RW High Speed Master Mode Code Address*/ #define IG4_REG_DATA_CMD 0x0010 /* RW Data Buffer and Command */ #define IG4_REG_SS_SCL_HCNT 0x0014 /* RW Std Speed clock High Count */ #define IG4_REG_SS_SCL_LCNT 0x0018 /* RW Std Speed clock Low Count */ @@ -91,8 +92,10 @@ #define IG4_REG_CLR_INTR 0x0040 /* RO Clear Interrupt */ #define IG4_REG_CLR_RX_UNDER 0x0044 /* RO Clear RX_Under Interrupt */ #define IG4_REG_CLR_RX_OVER 0x0048 /* RO Clear RX_Over Interrupt */ -#define IG4_REG_CLR_TX_OVER 0x004C /* RO Clear TX_Over Interrupt */ +#define IG4_REG_CLR_TX_OVER 0x004C /* RO Clear TX_Over Interrupt */i +#define IG4_REG_CLR_RD_REQ 0x0050 /* RO Clear RD_Req Interrupt */ #define IG4_REG_CLR_TX_ABORT 0x0054 /* RO Clear TX_Abort Interrupt */ +#define IG4_REG_CLR_RX_DONE 0x0058 /* RO Clear RX_Done Interrupt */ #define IG4_REG_CLR_ACTIVITY 0x005C /* RO Clear Activity Interrupt */ #define IG4_REG_CLR_STOP_DET 0x0060 /* RO Clear STOP Detection Int */ #define IG4_REG_CLR_START_DET 0x0064 /* RO Clear START Detection Int */ @@ -108,13 +111,26 @@ #define IG4_REG_DMA_TDLR 0x008C /* RW DMA Transmit Data Level */ #define IG4_REG_DMA_RDLR 0x0090 /* RW DMA Receive Data Level */ #define IG4_REG_SDA_SETUP 0x0094 /* RW SDA Setup */ +#define IG4_REG_ACK_GENERAL_CALL 0x0098 /* RW I2C ACK General Call */ #define IG4_REG_ENABLE_STATUS 0x009C /* RO Enable Status */ +/* Available at least on Atom SoCs and Haswell mobile. */ #define IG4_REG_COMP_PARAM1 0x00F4 /* RO Component Parameter */ #define IG4_REG_COMP_VER 0x00F8 /* RO Component Version */ +/* Available at least on Atom SoCs */ #define IG4_REG_COMP_TYPE 0x00FC /* RO Probe width/endian? (linux) */ +/* Available on Skylake-U/Y and Kaby Lake-U/Y */ +#define IG4_REG_RESETS_SKL 0x0204 /* RW Reset Register */ +#define IG4_REG_ACTIVE_LTR_VALUE 0x0210 /* RW Active LTR Value */ +#define IG4_REG_IDLE_LTR_VALUE 0x0214 /* RW Idle LTR Value */ +#define IG4_REG_TX_ACK_COUNT 0x0218 /* RO TX ACK Count */ +#define IG4_REG_RX_BYTE_COUNT 0x021C /* RO RX ACK Count */ +#define IG4_REG_DEVIDLE_CTRL 0x024C /* RW Device Control */ +/* Available at least on Atom SoCs */ #define IG4_REG_CLK_PARMS 0x0800 /* RW Clock Parameters */ -#define IG4_REG_RESETS 0x0804 /* RW Reset Register */ +/* Available at least on Atom SoCs and Haswell mobile */ +#define IG4_REG_RESETS_HSW 0x0804 /* RW Reset Register */ #define IG4_REG_GENERAL 0x0808 /* RW General Register */ +/* These LTR config registers are at least available on Haswell mobile. */ #define IG4_REG_SW_LTR_VALUE 0x0810 /* RW SW LTR Value */ #define IG4_REG_AUTO_LTR_VALUE 0x0814 /* RW Auto LTR Value */ @@ -566,8 +582,23 @@ * 10 (reserved) * 11 I2C host controller is in reset. */ -#define IG4_RESETS_ASSERT 0x0003 -#define IG4_RESETS_DEASSERT 0x0000 +#define IG4_RESETS_ASSERT_HSW 0x0003 +#define IG4_RESETS_DEASSERT_HSW 0x0000 + +/* Skylake-U/Y and Kaby Lake-U/Y have the reset bits inverted */ +#define IG4_RESETS_DEASSERT_SKL 0x0003 +#define IG4_RESETS_ASSERT_SKL 0x0000 + +/* Newer versions of the I2C controller allow to check whether + * the above ASSERT/DEASSERT is necessary by querying the DEVIDLE_CONTROL + * register. + * + * the RESTORE_REQUIRED bit can be cleared by writing 1 + * the DEVICE_IDLE status can be set to put the controller in an idle state + * */ + +#define IG4_RESTORE_REQUIRED 0x0008 +#define IG4_DEVICE_IDLE 0x0004 /* * GENERAL - (RW) General Reigster 22.2.38 --- b/sys/dev/ichiic/ig4_var.h +++ b/sys/dev/ichiic/ig4_var.h @@ -48,6 +48,12 @@ enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE }; +enum ig4_vers { + IG4_HASWELL = 0, + IG4_ATOM, + IG4_SKYLAKE, +}; + struct ig4iic_softc { device_t dev; struct intr_config_hook enum_hook; @@ -58,6 +64,7 @@ struct ig4iic_softc { int intr_rid; void *intr_handle; int intr_type; + enum ig4_vers version; enum ig4_op op; int cmd; int rnext;