FreeBSD Bugzilla – Attachment 207905 Details for
Bug 240485
ig4: Add Cannon Lake LP and H I2C Controller IDs
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
ig4 patchset rev.2
ig4.patch (text/plain), 47.55 KB, created by
Vladimir Kondratyev
on 2019-09-28 08:10:25 UTC
(
hide
)
Description:
ig4 patchset rev.2
Filename:
MIME Type:
Creator:
Vladimir Kondratyev
Created:
2019-09-28 08:10:25 UTC
Size:
47.55 KB
patch
obsolete
>diff --git a/sys/dev/ichiic/ig4_acpi.c b/sys/dev/ichiic/ig4_acpi.c >index f0b431fd1007..314c701cb8de 100644 >--- a/sys/dev/ichiic/ig4_acpi.c >+++ b/sys/dev/ichiic/ig4_acpi.c >@@ -69,18 +69,15 @@ static int > ig4iic_acpi_probe(device_t dev) > { > ig4iic_softc_t *sc; >- char *hid; > int rv; > > sc = device_get_softc(dev); > if (acpi_disabled("ig4iic")) > return (ENXIO); >- rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids, &hid); >+ rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids, NULL); > if (rv > 0) > return (rv); > >- if (strcmp("AMDI0010", hid) == 0) >- sc->access_intr_mask = 1; > device_set_desc(dev, "Designware I2C Controller"); > return (rv); > } >@@ -148,30 +145,59 @@ ig4iic_acpi_detach(device_t dev) > return (0); > } > >+static int >+ig4iic_acpi_suspend(device_t dev) >+{ >+ ig4iic_softc_t *sc = device_get_softc(dev); >+ >+ return (ig4iic_suspend(sc)); >+} >+ >+static int >+ig4iic_acpi_resume(device_t dev) >+{ >+ ig4iic_softc_t *sc = device_get_softc(dev); >+ >+ return (ig4iic_resume(sc)); >+} >+ > static device_method_t ig4iic_acpi_methods[] = { > /* Device interface */ > DEVMETHOD(device_probe, ig4iic_acpi_probe), > DEVMETHOD(device_attach, ig4iic_acpi_attach), > DEVMETHOD(device_detach, ig4iic_acpi_detach), >+ DEVMETHOD(device_suspend, ig4iic_acpi_suspend), >+ DEVMETHOD(device_resume, ig4iic_acpi_resume), >+ >+ /* Bus interface */ >+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), >+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), >+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), >+ DEVMETHOD(bus_release_resource, bus_generic_release_resource), >+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), >+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), >+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), >+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), >+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), > > /* iicbus interface */ > DEVMETHOD(iicbus_transfer, ig4iic_transfer), > DEVMETHOD(iicbus_reset, ig4iic_reset), >- DEVMETHOD(iicbus_callback, iicbus_null_callback), >+ DEVMETHOD(iicbus_callback, ig4iic_callback), > > DEVMETHOD_END > }; > > static driver_t ig4iic_acpi_driver = { >- "ig4iic_acpi", >+ "ig4iic", > ig4iic_acpi_methods, > sizeof(struct ig4iic_softc), > }; > > static devclass_t ig4iic_acpi_devclass; >-DRIVER_MODULE(ig4iic_acpi, acpi, ig4iic_acpi_driver, ig4iic_acpi_devclass, 0, 0); >+DRIVER_MODULE(ig4iic, acpi, ig4iic_acpi_driver, ig4iic_acpi_devclass, 0, 0); > >-MODULE_DEPEND(ig4iic_acpi, acpi, 1, 1, 1); >-MODULE_DEPEND(ig4iic_acpi, pci, 1, 1, 1); >-MODULE_DEPEND(ig4iic_acpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); >-MODULE_VERSION(ig4iic_acpi, 1); >+MODULE_DEPEND(ig4iic, acpi, 1, 1, 1); >+MODULE_DEPEND(ig4iic, pci, 1, 1, 1); >+MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); >+MODULE_VERSION(ig4iic, 1); >diff --git a/sys/dev/ichiic/ig4_iic.c b/sys/dev/ichiic/ig4_iic.c >index 54dba7b9657b..b785a6f3b711 100644 >--- a/sys/dev/ichiic/ig4_iic.c >+++ b/sys/dev/ichiic/ig4_iic.c >@@ -43,13 +43,17 @@ __FBSDID("$FreeBSD$"); > * See ig4_var.h for locking semantics. > */ > >+#include "opt_acpi.h" >+ > #include <sys/param.h> > #include <sys/systm.h> > #include <sys/kernel.h> > #include <sys/module.h> > #include <sys/errno.h> >+#include <sys/kdb.h> > #include <sys/lock.h> > #include <sys/mutex.h> >+#include <sys/proc.h> > #include <sys/sx.h> > #include <sys/syslog.h> > #include <sys/bus.h> >@@ -58,19 +62,105 @@ __FBSDID("$FreeBSD$"); > #include <machine/bus.h> > #include <sys/rman.h> > >-#include <dev/pci/pcivar.h> >-#include <dev/pci/pcireg.h> >+#ifdef DEV_ACPI >+#include <contrib/dev/acpica/include/acpi.h> >+#include <contrib/dev/acpica/include/accommon.h> >+#include <dev/acpica/acpivar.h> >+#endif >+ > #include <dev/iicbus/iicbus.h> > #include <dev/iicbus/iiconf.h> > > #include <dev/ichiic/ig4_reg.h> > #include <dev/ichiic/ig4_var.h> > >-#define TRANS_NORMAL 1 >-#define TRANS_PCALL 2 >-#define TRANS_BLOCK 3 >+#define DO_POLL(sc) (cold || kdb_active || SCHEDULER_STOPPED() || sc->poll) > >-static void ig4iic_start(void *xdev); >+/* >+ * Clock register values calculation formulas and timings are snarfed from >+ * Linux driver. >+ * *S_SCL_HCNT = IC clock rate * (tHIGH + SDA falling time) - 3 >+ * *S_SCL_LCNT = IC clock rate * (tLOW + SCL falling time) - 1 >+ * SDA_TX_HOLD = IC clock rate * SDA hold time >+ * All results are rounded to nearest. >+ * >+ * tLOW and tHIGH periods of the SCL clock are taken from I2C specification: >+ * Speed mode STD FAST FAST+ HIGH >+ * tHIGH 4.0 ns 0.6 ns 0.26 ns 0.06-0.12 ns >+ * tLOW 4.7 ns 1.3 ns 0.5 ns 0.16-0.32 ns >+ * >+ * HIGH Speed mode tHIGH/tLOW values are depend on bus capacitance. >+ */ >+static const struct ig4_cfg ig4iic_configs[] = { >+ [IG4_HASWELL] = { >+ .bus_speed = IG4_CTL_SPEED_FAST, >+ .ss_scl_hcnt = 432, >+ .ss_scl_lcnt = 507, >+ .fs_scl_hcnt = 110, >+ .fs_scl_lcnt = 160, >+ .sda_tx_hold = 9, >+ .txfifo_depth = 32, >+ .rxfifo_depth = 32, >+ }, >+ [IG4_ATOM] = { >+ .bus_speed = IG4_CTL_SPEED_FAST, >+ .ss_scl_hcnt = 512, >+ .ss_scl_lcnt = 512, >+ .fs_scl_hcnt = 85, >+ .fs_scl_lcnt = 153, >+ .sda_tx_hold = 6, >+ .txfifo_depth = 32, >+ .rxfifo_depth = 32, >+ }, >+ [IG4_SKYLAKE] = { >+ /* >+ * IC clock rate: 120 MHz >+ * SDA hold time: 230 ns >+ * SDA falling time: 300 ns >+ * SCL falling time: 300 ns >+ */ >+ .bus_speed = IG4_CTL_SPEED_FAST, >+ .ss_scl_hcnt = 513, >+ .ss_scl_lcnt = 599, >+ .fs_scl_hcnt = 105, >+ .fs_scl_lcnt = 191, >+ .sda_tx_hold = 28, >+ .txfifo_depth = 64, >+ .rxfifo_depth = 64, >+ }, >+ [IG4_APL] = { >+ /* >+ * IC clock rate: 133 MHz >+ * SDA hold time: 207 ns >+ * SDA falling time: 171 ns >+ * SCL falling time: 208 ns >+ */ >+ .bus_speed = IG4_CTL_SPEED_FAST, >+ .ss_scl_hcnt = 552, >+ .ss_scl_lcnt = 652, >+ .fs_scl_hcnt = 100, >+ .fs_scl_lcnt = 200, >+ .sda_tx_hold = 28, >+ }, >+ [IG4_CANNONLAKE] = { >+ /* >+ * IC clock rate: 216 MHz >+ * SDA hold time: 230 ns >+ * SDA falling time: 300 ns >+ * SCL falling time: 300 ns >+ */ >+ .bus_speed = IG4_CTL_SPEED_FAST, >+ .ss_scl_hcnt = 926, >+ .ss_scl_lcnt = 1079, >+ .fs_scl_hcnt = 192, >+ .fs_scl_lcnt = 345, >+ .sda_tx_hold = 50, >+ .txfifo_depth = 64, >+ .rxfifo_depth = 64, >+ }, >+}; >+ >+static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset); > static void ig4iic_intr(void *cookie); > static void ig4iic_dump(ig4iic_softc_t *sc); > >@@ -78,6 +168,10 @@ static int ig4_dump; > SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW, > &ig4_dump, 0, "Dump controller registers"); > >+static int ig4_timings; >+SYSCTL_INT(_debug, OID_AUTO, ig4_timings, CTLFLAG_RDTUN, &ig4_timings, 0, >+ "Controller timings 0=ACPI, 1=predefined, 2=legacy, 3=do not change"); >+ > /* > * Low-level inline support functions > */ >@@ -98,6 +192,64 @@ reg_read(ig4iic_softc_t *sc, uint32_t reg) > return (value); > } > >+static void >+set_intr_mask(ig4iic_softc_t *sc, uint32_t val) >+{ >+ if (sc->intr_mask != val) { >+ reg_write(sc, IG4_REG_INTR_MASK, val); >+ sc->intr_mask = val; >+ } >+} >+ >+static int >+intrstat2iic(ig4iic_softc_t *sc, uint32_t val) >+{ >+ uint32_t src; >+ >+ if (val & IG4_INTR_RX_UNDER) >+ reg_read(sc, IG4_REG_CLR_RX_UNDER); >+ if (val & IG4_INTR_RX_OVER) >+ reg_read(sc, IG4_REG_CLR_RX_OVER); >+ if (val & IG4_INTR_TX_OVER) >+ reg_read(sc, IG4_REG_CLR_TX_OVER); >+ >+ if (val & IG4_INTR_TX_ABRT) { >+ src = reg_read(sc, IG4_REG_TX_ABRT_SOURCE); >+ reg_read(sc, IG4_REG_CLR_TX_ABORT); >+ /* User-requested abort. Not really a error */ >+ if (src & IG4_ABRTSRC_TRANSFER) >+ return (IIC_ESTATUS); >+ /* Master has lost arbitration */ >+ if (src & IG4_ABRTSRC_ARBLOST) >+ return (IIC_EBUSBSY); >+ /* Did not receive an acknowledge from the remote slave */ >+ if (src & (IG4_ABRTSRC_TXNOACK_ADDR7 | >+ IG4_ABRTSRC_TXNOACK_ADDR10_1 | >+ IG4_ABRTSRC_TXNOACK_ADDR10_2 | >+ IG4_ABRTSRC_TXNOACK_DATA | >+ IG4_ABRTSRC_GENCALL_NOACK)) >+ return (IIC_ENOACK); >+ /* Programming errors */ >+ if (src & (IG4_ABRTSRC_GENCALL_READ | >+ IG4_ABRTSRC_NORESTART_START | >+ IG4_ABRTSRC_NORESTART_10)) >+ return (IIC_ENOTSUPP); >+ /* Other errors */ >+ if (src & IG4_ABRTSRC_ACKED_START) >+ return (IIC_EBUSERR); >+ } >+ /* >+ * TX_OVER, RX_OVER and RX_UNDER are caused by wrong RX/TX FIFO depth >+ * detection or driver's read/write pipelining errors. >+ */ >+ if (val & (IG4_INTR_TX_OVER | IG4_INTR_RX_OVER)) >+ return (IIC_EOVERFLOW); >+ if (val & IG4_INTR_RX_UNDER) >+ return (IIC_EUNDERFLOW); >+ >+ return (IIC_NOERR); >+} >+ > /* > * Enable or disable the controller and wait for the controller to acknowledge > * the state change. >@@ -113,12 +265,9 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl) > * When the controller is enabled, interrupt on STOP detect > * or receive character ready and clear pending interrupts. > */ >- if (ctl & IG4_I2C_ENABLE) { >- reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET | >- IG4_INTR_RX_FULL); >+ set_intr_mask(sc, 0); >+ if (ctl & IG4_I2C_ENABLE) > reg_read(sc, IG4_REG_CLR_INTR); >- } else >- reg_write(sc, IG4_REG_INTR_MASK, 0); > > reg_write(sc, IG4_REG_I2C_EN, ctl); > error = IIC_ETIMEOUT; >@@ -129,19 +278,16 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl) > error = 0; > break; > } >- if (cold) >- DELAY(1000); >- else >- mtx_sleep(sc, &sc->io_lock, 0, "i2cslv", 1); >+ pause("i2cslv", 1); > } > return (error); > } > > /* >- * Wait up to 25ms for the requested status using a 25uS polling loop. >+ * Wait up to 25ms for the requested interrupt using a 25uS polling loop. > */ > static int >-wait_status(ig4iic_softc_t *sc, uint32_t status) >+wait_intr(ig4iic_softc_t *sc, uint32_t intr) > { > uint32_t v; > int error; >@@ -151,25 +297,18 @@ wait_status(ig4iic_softc_t *sc, uint32_t status) > > error = IIC_ETIMEOUT; > >- for (;;) { >+ while (error == IIC_ETIMEOUT) { > /* > * Check requested status > */ >- v = reg_read(sc, IG4_REG_I2C_STA); >- if (v & status) { >- error = 0; >+ v = reg_read(sc, IG4_REG_RAW_INTR_STAT); >+ error = intrstat2iic(sc, v & IG4_INTR_ERR_MASK); >+ if (error) > break; >- } > >- /* >- * When waiting for receive data break-out if the interrupt >- * loaded data into the FIFO. >- */ >- if (status & IG4_STATUS_RX_NOTEMPTY) { >- if (sc->rpos != sc->rnext) { >- error = 0; >- break; >- } >+ if (v & intr) { >+ error = 0; >+ break; > } > > /* >@@ -177,7 +316,7 @@ wait_status(ig4iic_softc_t *sc, uint32_t status) > * reset the timeout if we see a change in the transmit > * FIFO level as progress is being made. > */ >- if (status & IG4_STATUS_TX_EMPTY) { >+ if (intr & (IG4_INTR_TX_EMPTY | IG4_INTR_STOP_DET)) { > v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK; > if (txlvl != v) { > txlvl = v; >@@ -188,45 +327,37 @@ wait_status(ig4iic_softc_t *sc, uint32_t status) > /* > * Stop if we've run out of time. > */ >- if (count_us >= limit_us) >+ if (count_us >= limit_us) { >+ error = IIC_ETIMEOUT; > break; >+ } > > /* >- * When waiting for receive data let the interrupt do its >- * work, otherwise poll with the lock held. >+ * When polling is not requested let the interrupt do its work. > */ >- if (status & IG4_STATUS_RX_NOTEMPTY) { >- mtx_sleep(sc, &sc->io_lock, 0, "i2cwait", >+ if (!DO_POLL(sc)) { >+ mtx_lock(&sc->io_lock); >+ sc->error = 0; >+ set_intr_mask(sc, intr | IG4_INTR_ERR_MASK); >+ error = mtx_sleep(sc, &sc->io_lock, 0, "i2cwait", > (hz + 99) / 100); /* sleep up to 10ms */ >+ if (error != 0) >+ error = IIC_ETIMEOUT; >+ else >+ error = sc->error; >+ set_intr_mask(sc, 0); >+ mtx_unlock(&sc->io_lock); > count_us += 10000; > } else { > DELAY(25); > count_us += 25; >+ error = IIC_ETIMEOUT; > } > } > > return (error); > } > >-/* >- * Read I2C data. The data might have already been read by >- * the interrupt code, otherwise it is sitting in the data >- * register. >- */ >-static uint8_t >-data_read(ig4iic_softc_t *sc) >-{ >- uint8_t c; >- >- if (sc->rpos == sc->rnext) { >- c = (uint8_t)reg_read(sc, IG4_REG_DATA_CMD); >- } else { >- c = sc->rbuf[sc->rpos & IG4_RBUFMASK]; >- ++sc->rpos; >- } >- return (c); >-} >- > /* > * Set the slave address. The controller must be disabled when > * changing the address. >@@ -250,22 +381,8 @@ set_slave_addr(ig4iic_softc_t *sc, uint8_t slave) > > /* > * Wait for TXFIFO to drain before disabling the controller. >- * >- * If a write message has not been completed it's really a >- * programming error, but for now in that case issue an extra >- * byte + STOP. >- * >- * If a read message has not been completed it's also a programming >- * error, for now just ignore it. > */ >- wait_status(sc, IG4_STATUS_TX_NOTFULL); >- if (sc->write_started) { >- reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_STOP); >- sc->write_started = 0; >- } >- if (sc->read_started) >- sc->read_started = 0; >- wait_status(sc, IG4_STATUS_TX_EMPTY); >+ wait_intr(sc, IG4_INTR_TX_EMPTY); > > set_controller(sc, 0); > ctl = reg_read(sc, IG4_REG_CTL); >@@ -288,48 +405,99 @@ set_slave_addr(ig4iic_softc_t *sc, uint8_t slave) > * IICBUS API FUNCTIONS > */ > static int >-ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave) >+ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave, bool repeated_start) > { > set_slave_addr(sc, slave >> 1); >+ >+ if (!repeated_start) { >+ /* >+ * Clear any previous TX/RX FIFOs overflow/underflow bits >+ * and I2C bus STOP condition. >+ */ >+ reg_read(sc, IG4_REG_CLR_INTR); >+ } >+ > return (0); > } > >+static int >+ig4iic_xfer_abort(ig4iic_softc_t *sc) >+{ >+ int error; >+ >+ /* Request send of STOP condition and flush of TX FIFO */ >+ set_controller(sc, IG4_I2C_ABORT | IG4_I2C_ENABLE); >+ /* >+ * Wait for the TX_ABRT interrupt with ABRTSRC_TRANSFER >+ * bit set in TX_ABRT_SOURCE register. >+ */ >+ error = wait_intr(sc, IG4_INTR_STOP_DET); >+ set_controller(sc, IG4_I2C_ENABLE); >+ >+ return (error == IIC_ESTATUS ? 0 : error); >+} >+ >+/* >+ * Amount of unread data before next burst to get better I2C bus utilization. >+ * 2 bytes is enough in FAST mode. 8 bytes is better in FAST+ and HIGH modes. >+ * Intel-recommended value is 16 for DMA transfers with 64-byte depth FIFOs. >+ */ >+#define IG4_FIFO_LOWAT 2 >+ > static int > ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, > bool repeated_start, bool stop) > { > uint32_t cmd; >- uint16_t i; >+ int requested = 0; >+ int received = 0; >+ int burst, target, lowat = 0; > int error; > > if (len == 0) > return (0); > >- cmd = IG4_DATA_COMMAND_RD; >- cmd |= repeated_start ? IG4_DATA_RESTART : 0; >- cmd |= stop && len == 1 ? IG4_DATA_STOP : 0; >- >- /* Issue request for the first byte (could be last as well). */ >- reg_write(sc, IG4_REG_DATA_CMD, cmd); >- >- for (i = 0; i < len; i++) { >- /* >- * Maintain a pipeline by queueing the allowance for the next >- * read before waiting for the current read. >- */ >- cmd = IG4_DATA_COMMAND_RD; >- if (i < len - 1) { >+ while (received < len) { >+ burst = sc->cfg.txfifo_depth - >+ (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK); >+ if (burst <= 0) { >+ error = wait_intr(sc, IG4_INTR_TX_EMPTY); >+ if (error) >+ break; >+ burst = sc->cfg.txfifo_depth; >+ } >+ /* Ensure we have enough free space in RXFIFO */ >+ burst = MIN(burst, sc->cfg.rxfifo_depth - lowat); >+ target = MIN(requested + burst, (int)len); >+ while (requested < target) { > cmd = IG4_DATA_COMMAND_RD; >- cmd |= stop && i == len - 2 ? IG4_DATA_STOP : 0; >+ if (repeated_start && requested == 0) >+ cmd |= IG4_DATA_RESTART; >+ if (stop && requested == len - 1) >+ cmd |= IG4_DATA_STOP; > reg_write(sc, IG4_REG_DATA_CMD, cmd); >+ requested++; >+ } >+ /* Leave some data queued to maintain the hardware pipeline */ >+ lowat = 0; >+ if (requested != len && requested - received > IG4_FIFO_LOWAT) >+ lowat = IG4_FIFO_LOWAT; >+ /* After TXFLR fills up, clear it by reading available data */ >+ while (received < requested - lowat) { >+ burst = MIN((int)len - received, >+ reg_read(sc, IG4_REG_RXFLR) & IG4_FIFOLVL_MASK); >+ if (burst > 0) { >+ while (burst--) >+ buf[received++] = 0xFF & >+ reg_read(sc, IG4_REG_DATA_CMD); >+ } else { >+ error = wait_intr(sc, IG4_INTR_RX_FULL); >+ if (error) >+ goto out; >+ } > } >- error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY); >- if (error) >- break; >- buf[i] = data_read(sc); > } >- >- (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); >+out: > return (error); > } > >@@ -338,24 +506,41 @@ ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, > bool repeated_start, bool stop) > { > uint32_t cmd; >- uint16_t i; >+ int sent = 0; >+ int burst, target; > int error; >+ bool lowat_set = false; > > if (len == 0) > return (0); > >- cmd = repeated_start ? IG4_DATA_RESTART : 0; >- for (i = 0; i < len; i++) { >- error = wait_status(sc, IG4_STATUS_TX_NOTFULL); >- if (error) >- break; >- cmd |= buf[i]; >- cmd |= stop && i == len - 1 ? IG4_DATA_STOP : 0; >- reg_write(sc, IG4_REG_DATA_CMD, cmd); >- cmd = 0; >+ while (sent < len) { >+ burst = sc->cfg.txfifo_depth - >+ (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK); >+ target = MIN(sent + burst, (int)len); >+ /* Leave some data queued to maintain the hardware pipeline */ >+ if (sent == 0 && target != len) { >+ lowat_set = true; >+ reg_write(sc, IG4_REG_TX_TL, IG4_FIFO_LOWAT); >+ } >+ while(sent < target) { >+ cmd = buf[sent]; >+ if (repeated_start && sent == 0) >+ cmd |= IG4_DATA_RESTART; >+ if (stop && sent == len - 1) >+ cmd |= IG4_DATA_STOP; >+ reg_write(sc, IG4_REG_DATA_CMD, cmd); >+ sent++; >+ } >+ if (sent < len) { >+ error = wait_intr(sc, IG4_INTR_TX_EMPTY); >+ if (error) >+ break; >+ } > } >+ if (lowat_set) >+ reg_write(sc, IG4_REG_TX_TL, 0); > >- (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); > return (error); > } > >@@ -369,6 +554,7 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) > int unit; > bool rpstart; > bool stop; >+ bool allocated; > > /* > * The hardware interface imposes limits on allowed I2C messages. >@@ -429,8 +615,10 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) > return (IIC_ENOTSUPP); > } > >- sx_xlock(&sc->call_lock); >- mtx_lock(&sc->io_lock); >+ /* Check if device is already allocated with iicbus_request_bus() */ >+ allocated = sx_xlocked(&sc->call_lock) != 0; >+ if (!allocated) >+ sx_xlock(&sc->call_lock); > > /* Debugging - dump registers. */ > if (ig4_dump) { >@@ -447,21 +635,11 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) > */ > reg_read(sc, IG4_REG_CLR_TX_ABORT); > >- /* >- * Clean out any previously received data. >- */ >- if (sc->rpos != sc->rnext && bootverbose) { >- device_printf(sc->dev, "discarding %d bytes of spurious data\n", >- sc->rnext - sc->rpos); >- } >- sc->rpos = 0; >- sc->rnext = 0; >- > rpstart = false; > error = 0; > for (i = 0; i < nmsgs; i++) { > if ((msgs[i].flags & IIC_M_NOSTART) == 0) { >- error = ig4iic_xfer_start(sc, msgs[i].slave); >+ error = ig4iic_xfer_start(sc, msgs[i].slave, rpstart); > } else { > if (!sc->slave_valid || > (msgs[i].slave >> 1) != sc->last_slave) { >@@ -482,14 +660,42 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) > else > error = ig4iic_write(sc, msgs[i].buf, msgs[i].len, > rpstart, stop); >- if (error != 0) >+ >+ /* Wait for error or stop condition occurred on the I2C bus */ >+ if (stop && error == 0) { >+ error = wait_intr(sc, IG4_INTR_STOP_DET); >+ if (error == 0) >+ reg_read(sc, IG4_REG_CLR_INTR); >+ } >+ >+ if (error != 0) { >+ /* >+ * Send STOP condition if it's not done yet and flush >+ * both FIFOs. Do a controller soft reset if transfer >+ * abort is failed. >+ */ >+ if (((reg_read(sc, IG4_REG_RAW_INTR_STAT) & >+ (IG4_INTR_START_DET | IG4_INTR_STOP_DET)) >+ == IG4_INTR_START_DET) && >+ ig4iic_xfer_abort(sc) != 0) { >+ device_printf(sc->dev, "Failed to abort " >+ "transfer. Do the controller reset.\n"); >+ ig4iic_set_config(sc, true); >+ } else { >+ while (reg_read(sc, IG4_REG_I2C_STA) & >+ IG4_STATUS_RX_NOTEMPTY) >+ reg_read(sc, IG4_REG_DATA_CMD); >+ reg_read(sc, IG4_REG_TX_ABRT_SOURCE); >+ reg_read(sc, IG4_REG_CLR_INTR); >+ } > break; >+ } > > rpstart = !stop; > } > >- mtx_unlock(&sc->io_lock); >- sx_unlock(&sc->call_lock); >+ if (!allocated) >+ sx_unlock(&sc->call_lock); > return (error); > } > >@@ -497,9 +703,11 @@ int > ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) > { > ig4iic_softc_t *sc = device_get_softc(dev); >+ bool allocated; > >- sx_xlock(&sc->call_lock); >- mtx_lock(&sc->io_lock); >+ allocated = sx_xlocked(&sc->call_lock) != 0; >+ if (!allocated) >+ sx_xlock(&sc->call_lock); > > /* TODO handle speed configuration? */ > if (oldaddr != NULL) >@@ -508,31 +716,214 @@ ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) > if (addr == IIC_UNKNOWN) > sc->slave_valid = false; > >- mtx_unlock(&sc->io_lock); >- sx_unlock(&sc->call_lock); >+ if (!allocated) >+ sx_unlock(&sc->call_lock); > return (0); > } > >-/* >- * Called from ig4iic_pci_attach/detach() >- */ > int >-ig4iic_attach(ig4iic_softc_t *sc) >+ig4iic_callback(device_t dev, int index, caddr_t data) >+{ >+ ig4iic_softc_t *sc = device_get_softc(dev); >+ int error = 0; >+ int how; >+ >+ /* >+ * Unfortunately, iicbus_request_bus() can return error code in both >+ * formats, POSIX and IIC. Here we use EAGAIN instead of IIC_EBUSBSY >+ * to match other possibly buggy iicbus_callback implementations. >+ * As iicbus_poll() returns IIC_EBUSBSY, caller should check returned >+ * value for both EAGAIN and IIC_EBUSBSY. Other error codes are POSIX. >+ */ >+ switch (index) { >+ case IIC_REQUEST_BUS: >+ /* force polling if ig4iic is requested with IIC_DONTWAIT */ >+ how = *(int *)data; >+ if ((how & IIC_WAIT) == 0) { >+ if (sx_try_xlock(&sc->call_lock) == 0) >+ error = EWOULDBLOCK; >+ else >+ sc->poll = true; >+ } else >+ sx_xlock(&sc->call_lock); >+ break; >+ >+ case IIC_RELEASE_BUS: >+ sc->poll = false; >+ sx_unlock(&sc->call_lock); >+ break; >+ >+ default: >+ error = EINVAL; >+ } >+ >+ return (error); >+} >+ >+#ifdef DEV_ACPI >+static int >+ig4iic_acpi_params(ig4iic_softc_t *sc, char *method, >+ uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_tx_hold) > { >+ ACPI_BUFFER buf; >+ ACPI_HANDLE handle; >+ ACPI_OBJECT *obj, *elems; > int error; >+ >+ handle = acpi_get_handle(sc->dev); >+ if (handle == NULL) >+ return (ENXIO); >+ >+ buf.Pointer = NULL; >+ buf.Length = ACPI_ALLOCATE_BUFFER; >+ >+ if (ACPI_FAILURE(AcpiEvaluateObject(handle, method, NULL, &buf))) >+ return (ENXIO); >+ >+ error = ENXIO; >+ obj = (ACPI_OBJECT *)buf.Pointer; >+ if (obj->Type == ACPI_TYPE_PACKAGE && obj->Package.Count == 3) { >+ elems = obj->Package.Elements; >+ *scl_hcnt = elems[0].Integer.Value & IG4_SCL_CLOCK_MASK; >+ *scl_lcnt = elems[1].Integer.Value & IG4_SCL_CLOCK_MASK; >+ *sda_tx_hold = elems[2].Integer.Value & IG4_SDA_TX_HOLD_MASK; >+ error = 0; >+ } >+ >+ AcpiOsFree(obj); >+ >+ return (error); >+} >+#endif /* DEV_ACPI */ >+ >+static void >+ig4iic_get_config(ig4iic_softc_t *sc) >+{ >+ const struct ig4_cfg *cfg; > uint32_t v; >+#ifdef DEV_ACPI >+ uint16_t sda_tx_hold; >+#endif > >- mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); >- sx_init(&sc->call_lock, "IG4 call lock"); >+ /* Fetch default hardware config from controller */ >+ sc->cfg.version = reg_read(sc, IG4_REG_COMP_VER); >+ sc->cfg.bus_speed = reg_read(sc, IG4_REG_CTL) & IG4_CTL_SPEED_MASK; >+ sc->cfg.ss_scl_hcnt = >+ reg_read(sc, IG4_REG_SS_SCL_HCNT) & IG4_SCL_CLOCK_MASK; >+ sc->cfg.ss_scl_lcnt = >+ reg_read(sc, IG4_REG_SS_SCL_LCNT) & IG4_SCL_CLOCK_MASK; >+ sc->cfg.fs_scl_hcnt = >+ reg_read(sc, IG4_REG_FS_SCL_HCNT) & IG4_SCL_CLOCK_MASK; >+ sc->cfg.fs_scl_lcnt = >+ reg_read(sc, IG4_REG_FS_SCL_LCNT) & IG4_SCL_CLOCK_MASK; >+ sc->cfg.sda_tx_hold = >+ reg_read(sc, IG4_REG_SDA_HOLD) & IG4_SDA_TX_HOLD_MASK; >+ >+ if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { >+ /* REG_COMP_PARAM1 register is not documented in Intel specs */ >+ v = reg_read(sc, IG4_REG_COMP_PARAM1); >+ if (IG4_PARAM1_TXFIFO_DEPTH(v) != 0) >+ sc->cfg.txfifo_depth = IG4_PARAM1_TXFIFO_DEPTH(v); >+ if (IG4_PARAM1_RXFIFO_DEPTH(v) != 0) >+ sc->cfg.rxfifo_depth = IG4_PARAM1_RXFIFO_DEPTH(v); >+ } else { >+ /* >+ * Hardware does not allow FIFO Threshold Levels value to be >+ * set larger than the depth of the buffer. If an attempt is >+ * made to do that, the actual value set will be the maximum >+ * depth of the buffer. >+ */ >+ v = reg_read(sc, IG4_REG_TX_TL); >+ reg_write(sc, IG4_REG_TX_TL, v | IG4_FIFO_MASK); >+ sc->cfg.txfifo_depth = >+ (reg_read(sc, IG4_REG_TX_TL) & IG4_FIFO_MASK) + 1; >+ reg_write(sc, IG4_REG_TX_TL, v); >+ v = reg_read(sc, IG4_REG_RX_TL); >+ reg_write(sc, IG4_REG_RX_TL, v | IG4_FIFO_MASK); >+ sc->cfg.rxfifo_depth = >+ (reg_read(sc, IG4_REG_RX_TL) & IG4_FIFO_MASK) + 1; >+ reg_write(sc, IG4_REG_RX_TL, v); >+ } >+ >+ /* Override hardware config with precalculated counter values */ >+ if (ig4_timings < 2 && sc->version < nitems(ig4iic_configs)) { >+ cfg = &ig4iic_configs[sc->version]; >+ if (cfg->bus_speed != 0) >+ sc->cfg.bus_speed = cfg->bus_speed; >+ if (cfg->ss_scl_hcnt != 0) >+ sc->cfg.ss_scl_hcnt = cfg->ss_scl_hcnt; >+ if (cfg->ss_scl_lcnt != 0) >+ sc->cfg.ss_scl_lcnt = cfg->ss_scl_lcnt; >+ if (cfg->fs_scl_hcnt != 0) >+ sc->cfg.fs_scl_hcnt = cfg->fs_scl_hcnt; >+ if (cfg->fs_scl_lcnt != 0) >+ sc->cfg.fs_scl_lcnt = cfg->fs_scl_lcnt; >+ if (cfg->sda_tx_hold != 0) >+ sc->cfg.sda_tx_hold = cfg->sda_tx_hold; >+ if (cfg->txfifo_depth != 0) >+ sc->cfg.txfifo_depth = cfg->txfifo_depth; >+ if (cfg->rxfifo_depth != 0) >+ sc->cfg.rxfifo_depth = cfg->rxfifo_depth; >+ } else if (ig4_timings == 2) { >+ /* >+ * Traditional (Haswell?) timings of original ig4 driver. >+ * Program based on a 25000 Hz clock. This is a bit of a >+ * hack (obviously). The defaults are 400 and 470 for standard >+ * and 60 and 130 for fast. The defaults for standard fail >+ * utterly (presumably cause an abort) because the clock time >+ * is ~18.8ms by default. This brings it down to ~4ms. >+ */ >+ sc->cfg.bus_speed = IG4_CTL_SPEED_STD; >+ sc->cfg.ss_scl_hcnt = 100; >+ sc->cfg.ss_scl_lcnt = 125; >+ sc->cfg.fs_scl_hcnt = 100; >+ sc->cfg.fs_scl_lcnt = 125; >+ /* XXX: SDA_HOLD=28 is valid for Skylake controllers only */ >+ if (IG4_HAS_ADDREGS(sc->version)) >+ sc->cfg.sda_tx_hold = 28; >+ sc->cfg.txfifo_depth = 16; /* Very conservative value */ >+ sc->cfg.rxfifo_depth = 16; >+ } >+ >+ if (sc->cfg.bus_speed != IG4_CTL_SPEED_STD) >+ sc->cfg.bus_speed = IG4_CTL_SPEED_FAST; >+ >+#ifdef DEV_ACPI >+ /* Evaluate SSCN and FMCN ACPI methods to fetch timings */ >+ if (ig4_timings == 0 && >+ ig4iic_acpi_params(sc, "SSCN", >+ &sc->cfg.ss_scl_hcnt, &sc->cfg.ss_scl_lcnt, &sda_tx_hold) == 0 && >+ sc->cfg.bus_speed == IG4_CTL_SPEED_STD && >+ sda_tx_hold != 0) >+ sc->cfg.sda_tx_hold = sda_tx_hold; >+ if (ig4_timings == 0 && >+ ig4iic_acpi_params(sc, "FMCN", >+ &sc->cfg.fs_scl_hcnt, &sc->cfg.fs_scl_lcnt, &sda_tx_hold) == 0 && >+ sc->cfg.bus_speed == IG4_CTL_SPEED_FAST && >+ sda_tx_hold != 0) >+ sc->cfg.sda_tx_hold = sda_tx_hold; >+#endif >+} >+ >+static int >+ig4iic_set_config(ig4iic_softc_t *sc, bool reset) >+{ >+ uint32_t v; > > v = reg_read(sc, IG4_REG_DEVIDLE_CTRL); >- if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) { >+ if (IG4_HAS_ADDREGS(sc->version) && (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); >+ pause("i2crst", 1); >+ reset = true; >+ } > >+ if ((sc->version == IG4_HASWELL || sc->version == IG4_ATOM) && reset) { >+ reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW); >+ reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW); >+ } else if (IG4_HAS_ADDREGS(sc->version) && reset) { > reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); > reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); >- DELAY(1000); > } > > if (sc->version == IG4_ATOM) >@@ -556,42 +947,31 @@ ig4iic_attach(ig4iic_softc_t *sc) > 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) { >+ } else if (IG4_HAS_ADDREGS(sc->version)) { > v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE); > v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE); > } > > if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { > v = reg_read(sc, IG4_REG_COMP_VER); >- if (v < IG4_COMP_MIN_VER) { >- error = ENXIO; >- goto done; >- } >+ if (v < IG4_COMP_MIN_VER) >+ return(ENXIO); > } >- 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); >- v = reg_read(sc, IG4_REG_FS_SCL_LCNT); >- v = reg_read(sc, IG4_REG_SDA_HOLD); > >- v = reg_read(sc, IG4_REG_SS_SCL_HCNT); >- reg_write(sc, IG4_REG_FS_SCL_HCNT, v); >- v = reg_read(sc, IG4_REG_SS_SCL_LCNT); >- reg_write(sc, IG4_REG_FS_SCL_LCNT, v); >+ if (set_controller(sc, 0)) { >+ device_printf(sc->dev, "controller error during attach-1\n"); >+ return (ENXIO); >+ } > >- /* >- * Program based on a 25000 Hz clock. This is a bit of a >- * hack (obviously). The defaults are 400 and 470 for standard >- * and 60 and 130 for fast. The defaults for standard fail >- * utterly (presumably cause an abort) because the clock time >- * is ~18.8ms by default. This brings it down to ~4ms (for now). >- */ >- reg_write(sc, IG4_REG_SS_SCL_HCNT, 100); >- reg_write(sc, IG4_REG_SS_SCL_LCNT, 125); >- reg_write(sc, IG4_REG_FS_SCL_HCNT, 100); >- reg_write(sc, IG4_REG_FS_SCL_LCNT, 125); >- if (sc->version == IG4_SKYLAKE) >- reg_write(sc, IG4_REG_SDA_HOLD, 28); >+ reg_read(sc, IG4_REG_CLR_INTR); >+ reg_write(sc, IG4_REG_INTR_MASK, 0); >+ sc->intr_mask = 0; >+ >+ reg_write(sc, IG4_REG_SS_SCL_HCNT, sc->cfg.ss_scl_hcnt); >+ reg_write(sc, IG4_REG_SS_SCL_LCNT, sc->cfg.ss_scl_lcnt); >+ reg_write(sc, IG4_REG_FS_SCL_HCNT, sc->cfg.fs_scl_hcnt); >+ reg_write(sc, IG4_REG_FS_SCL_LCNT, sc->cfg.fs_scl_lcnt); >+ reg_write(sc, IG4_REG_SDA_HOLD, sc->cfg.sda_tx_hold); > > /* > * Use a threshold of 1 so we get interrupted on each character, >@@ -600,13 +980,37 @@ ig4iic_attach(ig4iic_softc_t *sc) > * > * See ig4_var.h for details on interrupt handler synchronization. > */ >- reg_write(sc, IG4_REG_RX_TL, 1); >+ reg_write(sc, IG4_REG_RX_TL, 0); >+ reg_write(sc, IG4_REG_TX_TL, 0); > > reg_write(sc, IG4_REG_CTL, > IG4_CTL_MASTER | > IG4_CTL_SLAVE_DISABLE | > IG4_CTL_RESTARTEN | >- IG4_CTL_SPEED_STD); >+ (sc->cfg.bus_speed & IG4_CTL_SPEED_MASK)); >+ >+ /* Force setting of the target address on the next transfer */ >+ sc->slave_valid = 0; >+ >+ return (0); >+} >+ >+/* >+ * Called from ig4iic_pci_attach/detach() >+ */ >+int >+ig4iic_attach(ig4iic_softc_t *sc) >+{ >+ int error; >+ >+ mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); >+ sx_init(&sc->call_lock, "IG4 call lock"); >+ >+ ig4iic_get_config(sc); >+ >+ error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version)); >+ if (error) >+ goto done; > > sc->iicbus = device_add_child(sc->dev, "iicbus", -1); > if (sc->iicbus == NULL) { >@@ -615,25 +1019,16 @@ ig4iic_attach(ig4iic_softc_t *sc) > goto done; > } > >-#if 0 >- /* >- * Don't do this, it blows up the PCI config >- */ >- 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); >- } 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 >- >- mtx_lock(&sc->io_lock); >- if (set_controller(sc, 0)) >- device_printf(sc->dev, "controller error during attach-1\n"); >- if (set_controller(sc, IG4_I2C_ENABLE)) >+ if (set_controller(sc, IG4_I2C_ENABLE)) { > device_printf(sc->dev, "controller error during attach-2\n"); >- mtx_unlock(&sc->io_lock); >+ error = ENXIO; >+ goto done; >+ } >+ if (set_controller(sc, 0)) { >+ device_printf(sc->dev, "controller error during attach-3\n"); >+ error = ENXIO; >+ goto done; >+ } > error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, > NULL, ig4iic_intr, sc, &sc->intr_handle); > if (error) { >@@ -641,38 +1036,14 @@ ig4iic_attach(ig4iic_softc_t *sc) > "Unable to setup irq: error %d\n", error); > } > >- sc->enum_hook.ich_func = ig4iic_start; >- sc->enum_hook.ich_arg = sc->dev; >- >- /* >- * We have to wait until interrupts are enabled. I2C read and write >- * only works if the interrupts are available. >- */ >- if (config_intrhook_establish(&sc->enum_hook) != 0) >- error = ENOMEM; >- else >- error = 0; >- >-done: >- return (error); >-} >- >-void >-ig4iic_start(void *xdev) >-{ >- int error; >- ig4iic_softc_t *sc; >- device_t dev = (device_t)xdev; >- >- sc = device_get_softc(dev); >- >- config_intrhook_disestablish(&sc->enum_hook); >- > error = bus_generic_attach(sc->dev); > if (error) { > device_printf(sc->dev, > "failed to attach child: error %d\n", error); > } >+ >+done: >+ return (error); > } > > int >@@ -691,14 +1062,12 @@ ig4iic_detach(ig4iic_softc_t *sc) > bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); > > sx_xlock(&sc->call_lock); >- mtx_lock(&sc->io_lock); > > sc->iicbus = NULL; > sc->intr_handle = NULL; > reg_write(sc, IG4_REG_INTR_MASK, 0); > set_controller(sc, 0); > >- mtx_unlock(&sc->io_lock); > sx_xunlock(&sc->call_lock); > > mtx_destroy(&sc->io_lock); >@@ -707,6 +1076,47 @@ ig4iic_detach(ig4iic_softc_t *sc) > return (0); > } > >+int >+ig4iic_suspend(ig4iic_softc_t *sc) >+{ >+ int error; >+ >+ /* suspend all children */ >+ error = bus_generic_suspend(sc->dev); >+ >+ sx_xlock(&sc->call_lock); >+ set_controller(sc, 0); >+ if (IG4_HAS_ADDREGS(sc->version)) { >+ /* >+ * Place the device in the idle state, just to be safe >+ */ >+ reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE); >+ /* >+ * Controller can become unfunctional if I2C lines are pulled >+ * down when suspend procedure turns off power to I2C device. >+ * Place device in the reset state to avoid this. >+ */ >+ reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); >+ } >+ sx_xunlock(&sc->call_lock); >+ >+ return (error); >+} >+ >+int ig4iic_resume(ig4iic_softc_t *sc) >+{ >+ int error; >+ >+ sx_xlock(&sc->call_lock); >+ if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version))) >+ device_printf(sc->dev, "controller error during resume\n"); >+ sx_xunlock(&sc->call_lock); >+ >+ error = bus_generic_resume(sc->dev); >+ >+ return (error); >+} >+ > /* > * Interrupt Operation, see ig4_var.h for locking semantics. > */ >@@ -717,29 +1127,13 @@ ig4iic_intr(void *cookie) > uint32_t status; > > mtx_lock(&sc->io_lock); >-/* reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/ >- reg_read(sc, IG4_REG_CLR_INTR); >- status = reg_read(sc, IG4_REG_I2C_STA); >- while (status & IG4_STATUS_RX_NOTEMPTY) { >- sc->rbuf[sc->rnext & IG4_RBUFMASK] = >- (uint8_t)reg_read(sc, IG4_REG_DATA_CMD); >- ++sc->rnext; >- status = reg_read(sc, IG4_REG_I2C_STA); >- } >- >- /* >- * Workaround to trigger pending interrupt if IG4_REG_INTR_STAT >- * is changed after clearing it >- */ >- if (sc->access_intr_mask != 0) { >- status = reg_read(sc, IG4_REG_INTR_MASK); >- if (status != 0) { >- reg_write(sc, IG4_REG_INTR_MASK, 0); >- reg_write(sc, IG4_REG_INTR_MASK, status); >- } >- } >- >- wakeup(sc); >+ if (sc->intr_mask != 0) { >+ status = reg_read(sc, IG4_REG_INTR_STAT); >+ set_intr_mask(sc, 0); >+ sc->error = intrstat2iic(sc, status); >+ wakeup(sc); >+ } else >+ reg_write(sc, IG4_REG_INTR_MASK, 0); > mtx_unlock(&sc->io_lock); > } > >@@ -773,10 +1167,8 @@ ig4iic_dump(ig4iic_softc_t *sc) > REGDUMP(sc, IG4_REG_DMA_RDLR); > REGDUMP(sc, IG4_REG_SDA_SETUP); > REGDUMP(sc, IG4_REG_ENABLE_STATUS); >- if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { >- REGDUMP(sc, IG4_REG_COMP_PARAM1); >- REGDUMP(sc, IG4_REG_COMP_VER); >- } >+ 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); >@@ -790,12 +1182,11 @@ ig4iic_dump(ig4iic_softc_t *sc) > 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) { >+ } else if (IG4_HAS_ADDREGS(sc->version)) { > REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE); > REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE); > } > } > #undef REGDUMP > >-DRIVER_MODULE(iicbus, ig4iic_acpi, iicbus_driver, iicbus_devclass, NULL, NULL); >-DRIVER_MODULE(iicbus, ig4iic_pci, iicbus_driver, iicbus_devclass, NULL, NULL); >+DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL); >diff --git a/sys/dev/ichiic/ig4_pci.c b/sys/dev/ichiic/ig4_pci.c >index 8d7275ae57e6..ee996376fdcf 100644 >--- a/sys/dev/ichiic/ig4_pci.c >+++ b/sys/dev/ichiic/ig4_pci.c >@@ -90,6 +90,16 @@ static int ig4iic_pci_detach(device_t dev); > #define PCI_CHIP_APL_I2C_5 0x5ab68086 > #define PCI_CHIP_APL_I2C_6 0x5ab88086 > #define PCI_CHIP_APL_I2C_7 0x5aba8086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_0 0x9dc58086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_1 0x9dc68086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_2 0x9de88086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_3 0x9de98086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_4 0x9dea8086 >+#define PCI_CHIP_CANNONLAKE_LP_I2C_5 0x9deb8086 >+#define PCI_CHIP_CANNONLAKE_H_I2C_0 0xa3688086 >+#define PCI_CHIP_CANNONLAKE_H_I2C_1 0xa3698086 >+#define PCI_CHIP_CANNONLAKE_H_I2C_2 0xa36a8086 >+#define PCI_CHIP_CANNONLAKE_H_I2C_3 0xa36b8086 > > struct ig4iic_pci_device { > uint32_t devid; >@@ -121,7 +131,17 @@ static struct ig4iic_pci_device ig4iic_pci_devices[] = { > { PCI_CHIP_APL_I2C_4, "Intel Apollo Lake I2C Controller-4", IG4_APL}, > { PCI_CHIP_APL_I2C_5, "Intel Apollo Lake I2C Controller-5", IG4_APL}, > { PCI_CHIP_APL_I2C_6, "Intel Apollo Lake I2C Controller-6", IG4_APL}, >- { PCI_CHIP_APL_I2C_7, "Intel Apollo Lake I2C Controller-7", IG4_APL} >+ { PCI_CHIP_APL_I2C_7, "Intel Apollo Lake I2C Controller-7", IG4_APL}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_0, "Intel Cannon Lake-LP I2C Controller-0", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_1, "Intel Cannon Lake-LP I2C Controller-1", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_2, "Intel Cannon Lake-LP I2C Controller-2", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_3, "Intel Cannon Lake-LP I2C Controller-3", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_4, "Intel Cannon Lake-LP I2C Controller-4", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_LP_I2C_5, "Intel Cannon Lake-LP I2C Controller-5", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_H_I2C_0, "Intel Cannon Lake-H I2C Controller-0", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_H_I2C_1, "Intel Cannon Lake-H I2C Controller-1", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_H_I2C_2, "Intel Cannon Lake-H I2C Controller-2", IG4_CANNONLAKE}, >+ { PCI_CHIP_CANNONLAKE_H_I2C_3, "Intel Cannon Lake-H I2C Controller-3", IG4_CANNONLAKE}, > }; > > static int >@@ -206,31 +226,61 @@ ig4iic_pci_detach(device_t dev) > return (0); > } > >+static int >+ig4iic_pci_suspend(device_t dev) >+{ >+ ig4iic_softc_t *sc = device_get_softc(dev); >+ >+ return (ig4iic_suspend(sc)); >+} >+ >+static int >+ig4iic_pci_resume(device_t dev) >+{ >+ ig4iic_softc_t *sc = device_get_softc(dev); >+ >+ return (ig4iic_resume(sc)); >+} >+ > static device_method_t ig4iic_pci_methods[] = { > /* Device interface */ > DEVMETHOD(device_probe, ig4iic_pci_probe), > DEVMETHOD(device_attach, ig4iic_pci_attach), > DEVMETHOD(device_detach, ig4iic_pci_detach), >+ DEVMETHOD(device_suspend, ig4iic_pci_suspend), >+ DEVMETHOD(device_resume, ig4iic_pci_resume), >+ >+ /* Bus interface */ >+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), >+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), >+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), >+ DEVMETHOD(bus_release_resource, bus_generic_release_resource), >+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), >+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), >+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), >+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), >+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), > >+ /* iicbus interface */ > DEVMETHOD(iicbus_transfer, ig4iic_transfer), > DEVMETHOD(iicbus_reset, ig4iic_reset), >- DEVMETHOD(iicbus_callback, iicbus_null_callback), >+ DEVMETHOD(iicbus_callback, ig4iic_callback), > > DEVMETHOD_END > }; > > static driver_t ig4iic_pci_driver = { >- "ig4iic_pci", >+ "ig4iic", > ig4iic_pci_methods, > sizeof(struct ig4iic_softc) > }; > > static devclass_t ig4iic_pci_devclass; > >-DRIVER_MODULE_ORDERED(ig4iic_pci, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0, >+DRIVER_MODULE_ORDERED(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0, > SI_ORDER_ANY); >-MODULE_DEPEND(ig4iic_pci, pci, 1, 1, 1); >-MODULE_DEPEND(ig4iic_pci, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); >-MODULE_VERSION(ig4iic_pci, 1); >-MODULE_PNP_INFO("W32:vendor/device", pci, ig4iic_pci, ig4iic_pci_devices, >+MODULE_DEPEND(ig4iic, pci, 1, 1, 1); >+MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); >+MODULE_VERSION(ig4iic, 1); >+MODULE_PNP_INFO("W32:vendor/device", pci, ig4iic, ig4iic_pci_devices, > nitems(ig4iic_pci_devices)); >diff --git a/sys/dev/ichiic/ig4_reg.h b/sys/dev/ichiic/ig4_reg.h >index 45bc6aaed55f..27c8e311c818 100644 >--- a/sys/dev/ichiic/ig4_reg.h >+++ b/sys/dev/ichiic/ig4_reg.h >@@ -112,12 +112,12 @@ > #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. */ >+/* Available at least on Atom SoCs, Haswell mobile and some Skylakes. */ > #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 */ >+/* 0x200-0x2FF - Additional registers available on Skylake-U/Y and others */ > #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 */ >@@ -154,9 +154,12 @@ > #define IG4_CTL_SLAVE_DISABLE 0x0040 /* snarfed from linux */ > #define IG4_CTL_RESTARTEN 0x0020 /* Allow Restart when master */ > #define IG4_CTL_10BIT 0x0010 /* ctlr accepts 10-bit addresses */ >+#define IG4_CTL_SPEED_MASK 0x0006 /* speed at which the I2C operates */ >+#define IG4_CTL_MASTER 0x0001 /* snarfed from linux */ >+ >+#define IG4_CTL_SPEED_HIGH 0x0006 /* snarfed from linux */ > #define IG4_CTL_SPEED_FAST 0x0004 /* snarfed from linux */ > #define IG4_CTL_SPEED_STD 0x0002 /* snarfed from linux */ >-#define IG4_CTL_MASTER 0x0001 /* snarfed from linux */ > > /* > * TAR_ADD - Target Address Register 22.2.2 >@@ -325,6 +328,9 @@ > #define IG4_INTR_RX_OVER 0x0002 > #define IG4_INTR_RX_UNDER 0x0001 > >+#define IG4_INTR_ERR_MASK (IG4_INTR_TX_ABRT | IG4_INTR_TX_OVER | \ >+ IG4_INTR_RX_OVER | IG4_INTR_RX_UNDER) >+ > /* > * RX_TL - (RW) Receive FIFO Threshold Register 22.2.11 > * TX_TL - (RW) Transmit FIFO Threshold Register 22.2.12 >@@ -377,7 +383,9 @@ > * I2C_EN - (RW) I2C Enable Register 22.2.22 > * > * ABORT Software can abort an I2C transfer by setting this >- * bit. Hardware will clear the bit once the STOP has >+ * bit. In response, the controller issues the STOP >+ * condition over the I2C bus, followed by TX FIFO flush. >+ * Hardware will clear the bit once the STOP has > * been detected. This bit can only be set while the > * I2C interface is enabled. > * >@@ -406,14 +414,14 @@ > * FIFOs. Note that for some reason the mask is 9 bits instead of > * the 8 bits the fill level controls. > */ >-#define IG4_FIFOLVL_MASK 0x001F >+#define IG4_FIFOLVL_MASK 0x01FF > > /* > * SDA_HOLD - (RW) SDA Hold Time Length Register 22.2.26 > * > * Set the SDA hold time length register in I2C clocks. > */ >-#define IG4_SDA_HOLD_MASK 0x00FF >+#define IG4_SDA_TX_HOLD_MASK 0x0000FFFF > > /* > * TX_ABRT_SOURCE- (RO) Transmit Abort Source Register 22.2.27 >@@ -432,8 +440,8 @@ > #define IG4_ABRTSRC_NORESTART_10 0x00000400 /* RESTART disabled */ > #define IG4_ABRTSRC_NORESTART_START 0x00000200 /* RESTART disabled */ > #define IG4_ABRTSRC_ACKED_START 0x00000080 /* Improper acked START */ >-#define IG4_ABRTSRC_GENCALL_NOACK 0x00000020 /* Improper GENCALL */ >-#define IG4_ABRTSRC_GENCALL_READ 0x00000010 /* Nobody acked GENCALL */ >+#define IG4_ABRTSRC_GENCALL_READ 0x00000020 /* Improper GENCALL */ >+#define IG4_ABRTSRC_GENCALL_NOACK 0x00000010 /* Nobody acked GENCALL */ > #define IG4_ABRTSRC_TXNOACK_DATA 0x00000008 /* data phase no ACK */ > #define IG4_ABRTSRC_TXNOACK_ADDR10_2 0x00000004 /* addr10/1 phase no ACK */ > #define IG4_ABRTSRC_TXNOACK_ADDR10_1 0x00000002 /* addr10/2 phase no ACK */ >@@ -530,8 +538,8 @@ > * > * DATAW - Indicates the internal bus width in bits. > */ >-#define IG4_PARAM1_TXFIFO_DEPTH(v) (((v) >> 16) & 0xFF) >-#define IG4_PARAM1_RXFIFO_DEPTH(v) (((v) >> 8) & 0xFF) >+#define IG4_PARAM1_TXFIFO_DEPTH(v) ((((v) >> 16) & 0xFF) + 1) >+#define IG4_PARAM1_RXFIFO_DEPTH(v) ((((v) >> 8) & 0xFF) + 1) > #define IG4_PARAM1_CONFIG_VALID 0x00000080 > #define IG4_PARAM1_CONFIG_HASDMA 0x00000040 > #define IG4_PARAM1_CONFIG_INTR_IO 0x00000020 >diff --git a/sys/dev/ichiic/ig4_var.h b/sys/dev/ichiic/ig4_var.h >index 3250df5f7496..6c45e6766698 100644 >--- a/sys/dev/ichiic/ig4_var.h >+++ b/sys/dev/ichiic/ig4_var.h >@@ -43,15 +43,25 @@ > #include "pci_if.h" > #include "iicbus_if.h" > >-#define IG4_RBUFSIZE 128 >-#define IG4_RBUFMASK (IG4_RBUFSIZE - 1) >+enum ig4_vers { IG4_HASWELL, IG4_ATOM, IG4_SKYLAKE, IG4_APL, IG4_CANNONLAKE }; >+/* Controller has additional registers */ >+#define IG4_HAS_ADDREGS(vers) ((vers) == IG4_SKYLAKE || \ >+ (vers) == IG4_APL || (vers) == IG4_CANNONLAKE) > >-enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE }; >-enum ig4_vers { IG4_HASWELL, IG4_ATOM, IG4_SKYLAKE, IG4_APL }; >+struct ig4_cfg { >+ uint32_t version; >+ uint32_t bus_speed; >+ uint16_t ss_scl_hcnt; >+ uint16_t ss_scl_lcnt; >+ uint16_t fs_scl_hcnt; >+ uint16_t fs_scl_lcnt; >+ uint16_t sda_tx_hold; >+ int txfifo_depth; >+ int rxfifo_depth; >+}; > > struct ig4iic_softc { > device_t dev; >- struct intr_config_hook enum_hook; > device_t iicbus; > struct resource *regs_res; > int regs_rid; >@@ -60,40 +70,27 @@ struct ig4iic_softc { > void *intr_handle; > int intr_type; > enum ig4_vers version; >- enum ig4_op op; >+ struct ig4_cfg cfg; > int cmd; >- int rnext; >- int rpos; >- char rbuf[IG4_RBUFSIZE]; >+ uint32_t intr_mask; > int error; > uint8_t last_slave; > int platform_attached : 1; > int use_10bit : 1; > int slave_valid : 1; >- int read_started : 1; >- int write_started : 1; >- int access_intr_mask : 1; >+ int poll: 1; > > /* > * Locking semantics: > * > * Functions implementing the icbus interface that interact > * with the controller acquire an exclusive lock on call_lock >- * to prevent interleaving of calls to the interface and a lock on >- * io_lock right afterwards, to synchronize controller I/O activity. >- * >- * The interrupt handler can only read data while no iicbus call >- * is in progress or while io_lock is dropped during mtx_sleep in >- * wait_status and set_controller. It is safe to drop io_lock in those >- * places, because the interrupt handler only accesses those registers: >- * >- * - IG4_REG_I2C_STA (I2C Status) >- * - IG4_REG_DATA_CMD (Data Buffer and Command) >- * - IG4_REG_CLR_INTR (Clear Interrupt) >+ * to prevent interleaving of calls to the interface. > * >- * Locking outside of those places is required to make the content >- * of rpos/rnext predictable (e.g. whenever data_read is called and in >- * ig4iic_transfer). >+ * io_lock is used as condition variable to synchronize active process >+ * with the interrupt handler. It should not be used for tasks other >+ * than waiting for interrupt and passing parameters to and from >+ * it's handler. > */ > struct sx call_lock; > struct mtx io_lock; >@@ -104,9 +101,12 @@ typedef struct ig4iic_softc ig4iic_softc_t; > /* Attach/Detach called from ig4iic_pci_*() */ > int ig4iic_attach(ig4iic_softc_t *sc); > int ig4iic_detach(ig4iic_softc_t *sc); >+int ig4iic_suspend(ig4iic_softc_t *sc); >+int ig4iic_resume(ig4iic_softc_t *sc); > > /* iicbus methods */ > extern iicbus_transfer_t ig4iic_transfer; > extern iicbus_reset_t ig4iic_reset; >+extern iicbus_callback_t ig4iic_callback; > > #endif /* _ICHIIC_IG4_VAR_H_ */ >diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c >index a4417bc36252..969f9d665a9c 100644 >--- a/sys/dev/iicbus/iicbus.c >+++ b/sys/dev/iicbus/iicbus.c >@@ -330,6 +330,8 @@ static device_method_t iicbus_methods[] = { > DEVMETHOD(device_probe, iicbus_probe), > DEVMETHOD(device_attach, iicbus_attach), > DEVMETHOD(device_detach, iicbus_detach), >+ DEVMETHOD(device_suspend, bus_generic_suspend), >+ DEVMETHOD(device_resume, bus_generic_resume), > > /* bus interface */ > DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 240485
:
207365
|
207680
|
207704
|
207864
| 207905 |
208154