From owner-svn-src-stable@freebsd.org Sun Dec 22 00:46:08 2019 Return-Path: Delivered-To: svn-src-stable@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 26D5C1E14C7; Sun, 22 Dec 2019 00:46:08 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47gP1D0xgBz3L4x; Sun, 22 Dec 2019 00:46:08 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 16FAF1F883; Sun, 22 Dec 2019 00:46:08 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xBM0k8LY069228; Sun, 22 Dec 2019 00:46:08 GMT (envelope-from wulf@FreeBSD.org) Received: (from wulf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xBM0k7Lo069223; Sun, 22 Dec 2019 00:46:07 GMT (envelope-from wulf@FreeBSD.org) Message-Id: <201912220046.xBM0k7Lo069223@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wulf set sender to wulf@FreeBSD.org using -f From: Vladimir Kondratyev Date: Sun, 22 Dec 2019 00:46:07 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r355994 - in stable/12/sys/dev: chromebook_platform cyapa ichiic iicbus X-SVN-Group: stable-12 X-SVN-Commit-Author: wulf X-SVN-Commit-Paths: in stable/12/sys/dev: chromebook_platform cyapa ichiic iicbus X-SVN-Commit-Revision: 355994 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 22 Dec 2019 00:46:08 -0000 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 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 #include #include #include #include +#include #include #include +#include #include #include #include @@ -58,20 +62,67 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#ifdef DEV_ACPI +#include +#include +#include +#endif + #include #include #include #include -#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 ***