svn commit: r355994 - in stable/12/sys/dev: chromebook_platform cyapa ichiic iicbus
Vladimir Kondratyev
wulf at FreeBSD.org
Sun Dec 22 00:46:08 UTC 2019
Author: wulf
Date: Sun Dec 22 00:46:07 2019
New Revision: 355994
URL: https://svnweb.freebsd.org/changeset/base/355994
Log:
MFC r354291 - r354322, r354327, r355596
r354291:
[ig4] Give common name to PCI and ACPI device drivers
r354292:
[ig4] Handle controller startup errors
Obtained from: DragonflyBSD (509820b)
r354293:
[ig4] Only enable interrupts when we want them. Otherwise keep mask at 0.
Obtained from: DragonflyBSD (d7c8555)
r354294:
[ig4] Drop driver's internal RX FIFO
r354295:
[ig4] Do not wait for interrupts in set_controller() routine
r354296:
[ig4] Reduce scope of io_lock
r354297:
[ig4] Ignore stray interrupts
r354298:
[ig4] We actually need to set the Rx threshold register one smaller.
Obtained from: DragonflyBSD (02f0bf2)
r354299:
[ig4] Stop I2C controller after checking that it's kind of functional.
Obtained from: DragonfliBSD (0b3eedb)
r354300:
[ig4] disable controller before initialization of clock counters
r354301:
[ig4] Add support for polled mode
r354302:
[ig4] Allow enabling of polled mode from iicbus allocation callback
r354303:
[ig4] Do not wait until interrupts are enabled at attach stage
r354304:
[cyapa] Postpone start of the polling thread until sleep is available
r354305:
[ig4] dump IG4_REG_COMP_PARAM1 and IG4_REG_COMP_VER registers unconditionally
r354306:
[ig4] Set clock registers based on controller model
r354307:
[ig4] Implement burst mode for data reads
r354308:
[ig4] Add suspend/resume support
PR: 238037
r354309:
[ig4] Remove dead code inherited from DragonflyBSD
r354310:
[ig4] Rewrite ig4iic_write routine to use TX_EMPTY status flag
r354311:
[ig4] Convert last remaining usage of TX_NOTFULL status to TX_EMPTY
r354312:
[ig4] Use interrupts for waiting for empty TX FIFO
r354313:
[ig4] Convert polling loop from status-based to interrupt-based
r354314:
[ig4] Improve error detection
r354315:
[ig4] Set STOP condition and flush TX/RX FIFOs on error
r354316:
[ig4] On SkyLake controllers issue reset on attach unconditionally.
r354317:
[ig4] wait for bus stop condition after stop command issued
r354318:
[ig4] Minor improvement of write pipelining
r354319:
[ig4] Add generic resource methods to bus interface
r354320:
[ig4] Add support for CannonLake controllers
PR: 240485
Submitted by: Neel Chauhan <neel at neelc.org>
r354321:
[ig4] Enable additional registers support on Appolo Lake controllers
r354322:
[ig4] Convert ithread interrupt handler to filter based one.
r354327:
[ig4] Try to workaround MIPS namespace pollution issue
r355596:
[ig4] Remove unused methods from bus interface
Suggested by: jhb
Reviewed by: imp (previous version)
Differential Revision: https://reviews.freebsd.org/D22016
Modified:
stable/12/sys/dev/chromebook_platform/chromebook_platform.c
stable/12/sys/dev/cyapa/cyapa.c
stable/12/sys/dev/ichiic/ig4_acpi.c
stable/12/sys/dev/ichiic/ig4_iic.c
stable/12/sys/dev/ichiic/ig4_pci.c
stable/12/sys/dev/ichiic/ig4_reg.h
stable/12/sys/dev/ichiic/ig4_var.h
stable/12/sys/dev/iicbus/iicbus.c
Directory Properties:
stable/12/ (props changed)
Modified: stable/12/sys/dev/chromebook_platform/chromebook_platform.c
==============================================================================
--- stable/12/sys/dev/chromebook_platform/chromebook_platform.c Sun Dec 22 00:36:22 2019 (r355993)
+++ stable/12/sys/dev/chromebook_platform/chromebook_platform.c Sun Dec 22 00:46:07 2019 (r355994)
@@ -69,7 +69,7 @@ chromebook_i2c_identify(driver_t *driver, device_t bus
* See http://lxr.free-electrons.com/source/drivers/platform/chrome/chromeos_laptop.c
*/
controller = device_get_parent(bus);
- if (strcmp(device_get_name(controller), "ig4iic_pci") != 0)
+ if (strcmp(device_get_name(controller), "ig4iic") != 0)
return;
for (i = 0; i < nitems(slaves); i++) {
Modified: stable/12/sys/dev/cyapa/cyapa.c
==============================================================================
--- stable/12/sys/dev/cyapa/cyapa.c Sun Dec 22 00:36:22 2019 (r355993)
+++ stable/12/sys/dev/cyapa/cyapa.c Sun Dec 22 00:46:07 2019 (r355994)
@@ -152,6 +152,7 @@ struct cyapa_softc {
struct cdev *devnode;
struct selinfo selinfo;
struct mtx mutex;
+ struct intr_config_hook intr_hook;
int cap_resx;
int cap_resy;
@@ -419,6 +420,27 @@ done:
return (error);
}
+/*
+ * Start the polling thread
+ */
+static void
+cyapa_start(void *xdev)
+{
+ struct cyapa_softc *sc;
+ device_t dev = xdev;
+
+ sc = device_get_softc(dev);
+
+ config_intrhook_disestablish(&sc->intr_hook);
+
+ /* Setup input event tracking */
+ cyapa_set_power_mode(sc, CMD_POWER_MODE_IDLE);
+
+ /* Start the polling thread */
+ kthread_add(cyapa_poll_thread, sc, NULL, NULL,
+ 0, 0, "cyapa-poll");
+}
+
static int cyapa_probe(device_t);
static int cyapa_attach(device_t);
static int cyapa_detach(device_t);
@@ -536,12 +558,14 @@ cyapa_attach(device_t dev)
sc->mode.level = 0;
sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
- /* Setup input event tracking */
- cyapa_set_power_mode(sc, CMD_POWER_MODE_IDLE);
+ sc->intr_hook.ich_func = cyapa_start;
+ sc->intr_hook.ich_arg = sc->dev;
- /* Start the polling thread */
- kthread_add(cyapa_poll_thread, sc, NULL, NULL,
- 0, 0, "cyapa-poll");
+ /* Postpone start of the polling thread until sleep is available */
+ if (config_intrhook_establish(&sc->intr_hook) != 0) {
+ mtx_destroy(&sc->mutex);
+ return (ENOMEM);
+ }
sc->devnode = make_dev(&cyapa_cdevsw, unit,
UID_ROOT, GID_WHEEL, 0600, "cyapa%d", unit);
Modified: stable/12/sys/dev/ichiic/ig4_acpi.c
==============================================================================
--- stable/12/sys/dev/ichiic/ig4_acpi.c Sun Dec 22 00:36:22 2019 (r355993)
+++ stable/12/sys/dev/ichiic/ig4_acpi.c Sun Dec 22 00:46:07 2019 (r355994)
@@ -69,20 +69,15 @@ static int
ig4iic_acpi_probe(device_t dev)
{
ig4iic_softc_t *sc;
- char *hid;
sc = device_get_softc(dev);
if (acpi_disabled("ig4iic"))
return (ENXIO);
- hid = ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids);
- if (hid == NULL)
+ if (ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids) == NULL)
return (ENXIO);
- if (strcmp("AMDI0010", hid) == 0)
- sc->access_intr_mask = 1;
-
device_set_desc(dev, "Designware I2C Controller");
return (0);
}
@@ -150,30 +145,52 @@ 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),
+
/* 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);
-
-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);
+DRIVER_MODULE(ig4iic, acpi, ig4iic_acpi_driver, ig4iic_devclass, 0, 0);
+MODULE_DEPEND(ig4iic, acpi, 1, 1, 1);
Modified: stable/12/sys/dev/ichiic/ig4_iic.c
==============================================================================
--- stable/12/sys/dev/ichiic/ig4_iic.c Sun Dec 22 00:36:22 2019 (r355993)
+++ stable/12/sys/dev/ichiic/ig4_iic.c Sun Dec 22 00:46:07 2019 (r355994)
@@ -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,20 +62,67 @@ __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);
-static void ig4iic_intr(void *cookie);
+/*
+ * tLOW, tHIGH periods of the SCL clock and maximal falling time of both
+ * lines are taken from I2C specifications.
+ */
+#define IG4_SPEED_STD_THIGH 4000 /* nsec */
+#define IG4_SPEED_STD_TLOW 4700 /* nsec */
+#define IG4_SPEED_STD_TF_MAX 300 /* nsec */
+#define IG4_SPEED_FAST_THIGH 600 /* nsec */
+#define IG4_SPEED_FAST_TLOW 1300 /* nsec */
+#define IG4_SPEED_FAST_TF_MAX 300 /* nsec */
+
+/*
+ * Ig4 hardware parameters except Haswell are taken from intel_lpss driver
+ */
+static const struct ig4_hw ig4iic_hw[] = {
+ [IG4_HASWELL] = {
+ .ic_clock_rate = 100, /* MHz */
+ .sda_hold_time = 90, /* nsec */
+ .txfifo_depth = 32,
+ .rxfifo_depth = 32,
+ },
+ [IG4_ATOM] = {
+ .ic_clock_rate = 100,
+ .sda_fall_time = 280,
+ .scl_fall_time = 240,
+ .sda_hold_time = 60,
+ .txfifo_depth = 32,
+ .rxfifo_depth = 32,
+ },
+ [IG4_SKYLAKE] = {
+ .ic_clock_rate = 120,
+ .sda_hold_time = 230,
+ },
+ [IG4_APL] = {
+ .ic_clock_rate = 133,
+ .sda_fall_time = 171,
+ .scl_fall_time = 208,
+ .sda_hold_time = 207,
+ },
+ [IG4_CANNONLAKE] = {
+ .ic_clock_rate = 216,
+ .sda_hold_time = 230,
+ },
+};
+
+static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset);
+static driver_filter_t ig4iic_intr;
static void ig4iic_dump(ig4iic_softc_t *sc);
static int ig4_dump;
@@ -79,6 +130,17 @@ SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW,
&ig4_dump, 0, "Dump controller registers");
/*
+ * Clock registers initialization control
+ * 0 - Try read clock registers from ACPI and fallback to p.1.
+ * 1 - Calculate values based on controller type (IC clock rate).
+ * 2 - Use values inherited from DragonflyBSD driver (old behavior).
+ * 3 - Keep clock registers intact.
+ */
+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
*/
static __inline void
@@ -98,6 +160,64 @@ reg_read(ig4iic_softc_t *sc, uint32_t reg)
return (value);
}
+static void
+ig4iic_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 +233,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);
+ ig4iic_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 +246,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;
@@ -149,35 +263,21 @@ wait_status(ig4iic_softc_t *sc, uint32_t status)
u_int count_us = 0;
u_int limit_us = 25000; /* 25ms */
- error = IIC_ETIMEOUT;
-
for (;;) {
/*
* 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 || (v & intr))
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;
- }
- }
-
- /*
* When waiting for the transmit FIFO to become empty,
* 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,16 +288,21 @@ 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_spin(&sc->io_lock);
+ ig4iic_set_intr_mask(sc, intr | IG4_INTR_ERR_MASK);
+ msleep_spin(sc, &sc->io_lock, "i2cwait",
(hz + 99) / 100); /* sleep up to 10ms */
+ ig4iic_set_intr_mask(sc, 0);
+ mtx_unlock_spin(&sc->io_lock);
count_us += 10000;
} else {
DELAY(25);
@@ -209,25 +314,6 @@ wait_status(ig4iic_softc_t *sc, uint32_t status)
}
/*
- * 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 +336,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 +360,110 @@ 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 bool
+ig4iic_xfer_is_started(ig4iic_softc_t *sc)
+{
+ /*
+ * It requires that no IG4_REG_CLR_INTR or IG4_REG_CLR_START/STOP_DET
+ * register reads is issued after START condition.
+ */
+ return ((reg_read(sc, IG4_REG_RAW_INTR_STAT) &
+ (IG4_INTR_START_DET | IG4_INTR_STOP_DET)) == IG4_INTR_START_DET);
+}
+
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++;
}
- error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY);
- if (error)
- break;
- buf[i] = data_read(sc);
+ /* 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;
+ }
+ }
}
-
- (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
+out:
return (error);
}
@@ -338,24 +472,41 @@ ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_
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 (!lowat_set && 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 +520,7 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
int unit;
bool rpstart;
bool stop;
+ bool allocated;
/*
* The hardware interface imposes limits on allowed I2C messages.
@@ -429,8 +581,10 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
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 +601,11 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
*/
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 +626,40 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
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 (ig4iic_xfer_is_started(sc) &&
+ 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 +667,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 +680,255 @@ ig4iic_reset(device_t dev, u_char speed, u_char addr,
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);
}
+int
+ig4iic_callback(device_t dev, int index, caddr_t data)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error = 0;
+ int how;
+
+ 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 = IIC_EBUSBSY;
+ 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 = errno2iic(EINVAL);
+ }
+
+ return (error);
+}
+
/*
- * Called from ig4iic_pci_attach/detach()
+ * Clock register values can be calculated with following rough equations:
+ * SCL_HCNT = ceil(IC clock rate * tHIGH)
+ * SCL_LCNT = ceil(IC clock rate * tLOW)
+ * SDA_HOLD = ceil(IC clock rate * SDA hold time)
+ * Precise equations take signal's falling, rising and spike suppression
+ * times in to account. They can be found in Synopsys or Intel documentation.
+ *
+ * Here we snarf formulas and defaults from Linux driver to be able to use
+ * timing values provided by Intel LPSS driver "as is".
*/
-int
-ig4iic_attach(ig4iic_softc_t *sc)
+static int
+ig4iic_clk_params(const struct ig4_hw *hw, int speed,
+ uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
{
- int error;
+ uint32_t thigh, tlow, tf_max; /* nsec */
+ uint32_t sda_fall_time; /* nsec */
+ uint32_t scl_fall_time; /* nsec */
+
+ switch (speed) {
+ case IG4_CTL_SPEED_STD:
+ thigh = IG4_SPEED_STD_THIGH;
+ tlow = IG4_SPEED_STD_TLOW;
+ tf_max = IG4_SPEED_STD_TF_MAX;
+ break;
+
+ case IG4_CTL_SPEED_FAST:
+ thigh = IG4_SPEED_FAST_THIGH;
+ tlow = IG4_SPEED_FAST_TLOW;
+ tf_max = IG4_SPEED_FAST_TF_MAX;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ /* Use slowest falling time defaults to be on the safe side */
+ sda_fall_time = hw->sda_fall_time == 0 ? tf_max : hw->sda_fall_time;
+ *scl_hcnt = (uint16_t)
+ ((hw->ic_clock_rate * (thigh + sda_fall_time) + 500) / 1000 - 3);
+
+ scl_fall_time = hw->scl_fall_time == 0 ? tf_max : hw->scl_fall_time;
+ *scl_lcnt = (uint16_t)
+ ((hw->ic_clock_rate * (tlow + scl_fall_time) + 500) / 1000 - 1);
+
+ /*
+ * There is no "known good" default value for tHD;DAT so keep SDA_HOLD
+ * intact if sda_hold_time value is not provided.
+ */
+ if (hw->sda_hold_time != 0)
+ *sda_hold = (uint16_t)
+ ((hw->ic_clock_rate * hw->sda_hold_time + 500) / 1000);
+
+ return (0);
+}
+
+#ifdef DEV_ACPI
+static ACPI_STATUS
+ig4iic_acpi_params(ACPI_HANDLE handle, char *method,
+ uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
+{
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *obj, *elems;
+ ACPI_STATUS status;
+
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+
+ status = AcpiEvaluateObject(handle, method, NULL, &buf);
+ if (ACPI_FAILURE(status))
+ return (status);
+
+ status = AE_TYPE;
+ 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_hold = elems[2].Integer.Value & IG4_SDA_TX_HOLD_MASK;
+ status = AE_OK;
+ }
+
+ AcpiOsFree(obj);
+
+ return (status);
+}
+#endif /* DEV_ACPI */
+
+static void
+ig4iic_get_config(ig4iic_softc_t *sc)
+{
+ const struct ig4_hw *hw;
uint32_t v;
+#ifdef DEV_ACPI
+ ACPI_HANDLE handle;
+#endif
+ /* 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.ss_sda_hold = sc->cfg.fs_sda_hold =
+ reg_read(sc, IG4_REG_SDA_HOLD) & IG4_SDA_TX_HOLD_MASK;
- mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
- sx_init(&sc->call_lock, "IG4 call lock");
+ if (sc->cfg.bus_speed != IG4_CTL_SPEED_STD)
+ sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
+ /* REG_COMP_PARAM1 is not documented in latest Intel specs */
+ if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
+ 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 IC_clock-based counter values */
+ if (ig4_timings < 2 && sc->version < nitems(ig4iic_hw)) {
+ hw = &ig4iic_hw[sc->version];
+ sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
+ ig4iic_clk_params(hw, IG4_CTL_SPEED_STD, &sc->cfg.ss_scl_hcnt,
+ &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
+ ig4iic_clk_params(hw, IG4_CTL_SPEED_FAST, &sc->cfg.fs_scl_hcnt,
+ &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
+ if (hw->txfifo_depth != 0)
+ sc->cfg.txfifo_depth = hw->txfifo_depth;
+ if (hw->rxfifo_depth != 0)
+ sc->cfg.rxfifo_depth = hw->rxfifo_depth;
+ } else if (ig4_timings == 2) {
+ /*
+ * 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 = sc->cfg.fs_scl_hcnt = 100;
+ sc->cfg.ss_scl_lcnt = sc->cfg.fs_scl_lcnt = 125;
+ if (sc->version == IG4_SKYLAKE)
+ sc->cfg.ss_sda_hold = sc->cfg.fs_sda_hold = 28;
+ }
+
+#ifdef DEV_ACPI
+ /* Evaluate SSCN and FMCN ACPI methods to fetch timings */
+ if (ig4_timings == 0 && (handle = acpi_get_handle(sc->dev)) != NULL) {
+ ig4iic_acpi_params(handle, "SSCN", &sc->cfg.ss_scl_hcnt,
+ &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
+ ig4iic_acpi_params(handle, "FMCN", &sc->cfg.fs_scl_hcnt,
+ &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable
mailing list