[ig4] Add support for i2c controllers on Skylake and Kaby Lake

This was tested by Ben on  HP Chromebook 13 G1 with a
Skylake CPU and Sunrise Point-LP I2C controller and by me on
Minnowboard Turbot with Atom E3826 (formerly Bay Trail)

Submitted by:	Ben Pye <ben@curlybracket.co.uk>
Reviewed by:	gonzo
Obtained from:	DragonflyBSD (a4549657 by Imre Vadász)
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D13654
This commit is contained in:
Oleksandr Tymoshenko 2018-03-06 23:39:43 +00:00
parent 9f3b313382
commit b3e8ee5d05
5 changed files with 116 additions and 25 deletions

@ -85,6 +85,8 @@ ig4iic_acpi_attach(device_t dev)
sc = device_get_softc(dev); sc = device_get_softc(dev);
sc->dev = dev; sc->dev = dev;
/* All the HIDs matched are Atom SOCs. */
sc->version = IG4_ATOM;
sc->regs_rid = 0; sc->regs_rid = 0;
sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->regs_rid, RF_ACTIVE); &sc->regs_rid, RF_ACTIVE);

@ -525,22 +525,38 @@ ig4iic_attach(ig4iic_softc_t *sc)
mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
sx_init(&sc->call_lock, "IG4 call lock"); sx_init(&sc->call_lock, "IG4 call lock");
v = reg_read(sc, IG4_REG_COMP_TYPE); if (sc->version == IG4_ATOM)
v = reg_read(sc, IG4_REG_COMP_PARAM1); v = reg_read(sc, IG4_REG_COMP_TYPE);
v = reg_read(sc, IG4_REG_GENERAL);
if ((v & IG4_GENERAL_SWMODE) == 0) { if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
v |= IG4_GENERAL_SWMODE; v = reg_read(sc, IG4_REG_COMP_PARAM1);
reg_write(sc, IG4_REG_GENERAL, v);
v = reg_read(sc, IG4_REG_GENERAL); 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); if (sc->version == IG4_HASWELL) {
v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); 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 (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
if (v != IG4_COMP_VER) { v = reg_read(sc, IG4_REG_COMP_VER);
error = ENXIO; if (v != IG4_COMP_VER) {
goto done; error = ENXIO;
goto done;
}
} }
v = reg_read(sc, IG4_REG_SS_SCL_HCNT); v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
v = reg_read(sc, IG4_REG_SS_SCL_LCNT); v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
@ -591,8 +607,13 @@ ig4iic_attach(ig4iic_softc_t *sc)
/* /*
* Don't do this, it blows up the PCI config * Don't do this, it blows up the PCI config
*/ */
reg_write(sc, IG4_REG_RESETS, IG4_RESETS_ASSERT); if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
reg_write(sc, IG4_REG_RESETS, IG4_RESETS_DEASSERT); reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
} else if (sc->version = IG4_SKYLAKE) {
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
}
#endif #endif
mtx_lock(&sc->io_lock); mtx_lock(&sc->io_lock);
@ -727,14 +748,27 @@ ig4iic_dump(ig4iic_softc_t *sc)
REGDUMP(sc, IG4_REG_DMA_RDLR); REGDUMP(sc, IG4_REG_DMA_RDLR);
REGDUMP(sc, IG4_REG_SDA_SETUP); REGDUMP(sc, IG4_REG_SDA_SETUP);
REGDUMP(sc, IG4_REG_ENABLE_STATUS); REGDUMP(sc, IG4_REG_ENABLE_STATUS);
REGDUMP(sc, IG4_REG_COMP_PARAM1); if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
REGDUMP(sc, IG4_REG_COMP_VER); REGDUMP(sc, IG4_REG_COMP_PARAM1);
REGDUMP(sc, IG4_REG_COMP_TYPE); REGDUMP(sc, IG4_REG_COMP_VER);
REGDUMP(sc, IG4_REG_CLK_PARMS); }
REGDUMP(sc, IG4_REG_RESETS); if (sc->version == IG4_ATOM) {
REGDUMP(sc, IG4_REG_GENERAL); REGDUMP(sc, IG4_REG_COMP_TYPE);
REGDUMP(sc, IG4_REG_SW_LTR_VALUE); REGDUMP(sc, IG4_REG_CLK_PARMS);
REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE); }
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 #undef REGDUMP

@ -74,34 +74,74 @@ static int ig4iic_pci_detach(device_t dev);
#define PCI_CHIP_BRASWELL_I2C_5 0x22c58086 #define PCI_CHIP_BRASWELL_I2C_5 0x22c58086
#define PCI_CHIP_BRASWELL_I2C_6 0x22c68086 #define PCI_CHIP_BRASWELL_I2C_6 0x22c68086
#define PCI_CHIP_BRASWELL_I2C_7 0x22c78086 #define PCI_CHIP_BRASWELL_I2C_7 0x22c78086
#define PCI_CHIP_SKYLAKE_I2C_0 0x9d608086
#define PCI_CHIP_SKYLAKE_I2C_1 0x9d618086
#define PCI_CHIP_SKYLAKE_I2C_2 0x9d628086
#define PCI_CHIP_SKYLAKE_I2C_3 0x9d638086
#define PCI_CHIP_SKYLAKE_I2C_4 0x9d648086
#define PCI_CHIP_SKYLAKE_I2C_5 0x9d658086
static int static int
ig4iic_pci_probe(device_t dev) ig4iic_pci_probe(device_t dev)
{ {
ig4iic_softc_t *sc = device_get_softc(dev);
switch(pci_get_devid(dev)) { switch(pci_get_devid(dev)) {
case PCI_CHIP_LYNXPT_LP_I2C_1: case PCI_CHIP_LYNXPT_LP_I2C_1:
device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1"); device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1");
sc->version = IG4_HASWELL;
break; break;
case PCI_CHIP_LYNXPT_LP_I2C_2: case PCI_CHIP_LYNXPT_LP_I2C_2:
device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2"); device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2");
sc->version = IG4_HASWELL;
break; break;
case PCI_CHIP_BRASWELL_I2C_1: case PCI_CHIP_BRASWELL_I2C_1:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 1"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 1");
sc->version = IG4_ATOM;
break; break;
case PCI_CHIP_BRASWELL_I2C_2: case PCI_CHIP_BRASWELL_I2C_2:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 2"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 2");
sc->version = IG4_ATOM;
break; break;
case PCI_CHIP_BRASWELL_I2C_3: case PCI_CHIP_BRASWELL_I2C_3:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 3"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 3");
sc->version = IG4_ATOM;
break; break;
case PCI_CHIP_BRASWELL_I2C_5: case PCI_CHIP_BRASWELL_I2C_5:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 5"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 5");
sc->version = IG4_ATOM;
break; break;
case PCI_CHIP_BRASWELL_I2C_6: case PCI_CHIP_BRASWELL_I2C_6:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 6"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 6");
sc->version = IG4_ATOM;
break; break;
case PCI_CHIP_BRASWELL_I2C_7: case PCI_CHIP_BRASWELL_I2C_7:
device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 7"); device_set_desc(dev, "Intel Braswell Serial I/O I2C Port 7");
sc->version = IG4_ATOM;
break;
case PCI_CHIP_SKYLAKE_I2C_0:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-0");
sc->version = IG4_SKYLAKE;
break;
case PCI_CHIP_SKYLAKE_I2C_1:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-1");
sc->version = IG4_SKYLAKE;
break;
case PCI_CHIP_SKYLAKE_I2C_2:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-2");
sc->version = IG4_SKYLAKE;
break;
case PCI_CHIP_SKYLAKE_I2C_3:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-3");
sc->version = IG4_SKYLAKE;
break;
case PCI_CHIP_SKYLAKE_I2C_4:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-4");
sc->version = IG4_SKYLAKE;
break;
case PCI_CHIP_SKYLAKE_I2C_5:
device_set_desc(dev, "Intel Sunrise Point-LP I2C Controller-5");
sc->version = IG4_SKYLAKE;
break; break;
default: default:
return (ENXIO); return (ENXIO);

@ -109,12 +109,21 @@
#define IG4_REG_DMA_RDLR 0x0090 /* RW DMA Receive 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_SDA_SETUP 0x0094 /* RW SDA Setup */
#define IG4_REG_ENABLE_STATUS 0x009C /* RO Enable Status */ #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_PARAM1 0x00F4 /* RO Component Parameter */
#define IG4_REG_COMP_VER 0x00F8 /* RO Component Version */ #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) */ #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 */
/* Available at least on Atom SoCs */
#define IG4_REG_CLK_PARMS 0x0800 /* RW Clock Parameters */ #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 */ #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_SW_LTR_VALUE 0x0810 /* RW SW LTR Value */
#define IG4_REG_AUTO_LTR_VALUE 0x0814 /* RW Auto LTR Value */ #define IG4_REG_AUTO_LTR_VALUE 0x0814 /* RW Auto LTR Value */
@ -566,8 +575,12 @@
* 10 (reserved) * 10 (reserved)
* 11 I2C host controller is in reset. * 11 I2C host controller is in reset.
*/ */
#define IG4_RESETS_ASSERT 0x0003 #define IG4_RESETS_ASSERT_HSW 0x0003
#define IG4_RESETS_DEASSERT 0x0000 #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
/* /*
* GENERAL - (RW) General Reigster 22.2.38 * GENERAL - (RW) General Reigster 22.2.38

@ -47,6 +47,7 @@
#define IG4_RBUFMASK (IG4_RBUFSIZE - 1) #define IG4_RBUFMASK (IG4_RBUFSIZE - 1)
enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE }; enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE };
enum ig4_vers { IG4_HASWELL, IG4_ATOM, IG4_SKYLAKE };
struct ig4iic_softc { struct ig4iic_softc {
device_t dev; device_t dev;
@ -58,6 +59,7 @@ struct ig4iic_softc {
int intr_rid; int intr_rid;
void *intr_handle; void *intr_handle;
int intr_type; int intr_type;
enum ig4_vers version;
enum ig4_op op; enum ig4_op op;
int cmd; int cmd;
int rnext; int rnext;