From owner-svn-src-head@freebsd.org Thu Jun 29 01:59:40 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A5B44D8C425; Thu, 29 Jun 2017 01:59:40 +0000 (UTC) (envelope-from ian@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 mx1.freebsd.org (Postfix) with ESMTPS id 8193C6FD2D; Thu, 29 Jun 2017 01:59:40 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v5T1xd8H031166; Thu, 29 Jun 2017 01:59:39 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v5T1xdXR031165; Thu, 29 Jun 2017 01:59:39 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201706290159.v5T1xdXR031165@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Thu, 29 Jun 2017 01:59:39 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r320462 - head/sys/arm/freescale/imx X-SVN-Group: head X-SVN-Commit-Author: ian X-SVN-Commit-Paths: head/sys/arm/freescale/imx X-SVN-Commit-Revision: 320462 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 29 Jun 2017 01:59:40 -0000 Author: ian Date: Thu Jun 29 01:59:39 2017 New Revision: 320462 URL: https://svnweb.freebsd.org/changeset/base/320462 Log: Add bus recovery handling to the imx5/imx6 i2c driver. Modified: head/sys/arm/freescale/imx/imx_i2c.c Modified: head/sys/arm/freescale/imx/imx_i2c.c ============================================================================== --- head/sys/arm/freescale/imx/imx_i2c.c Thu Jun 29 01:50:58 2017 (r320461) +++ head/sys/arm/freescale/imx/imx_i2c.c Thu Jun 29 01:59:39 2017 (r320462) @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -61,12 +62,16 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "iicbus_if.h" #include #include #include +#include +#include + #define I2C_ADDR_REG 0x00 /* I2C slave address register */ #define I2C_FDR_REG 0x04 /* I2C frequency divider register */ #define I2C_CONTROL_REG 0x08 /* I2C control register */ @@ -130,6 +135,9 @@ struct i2c_softc { struct resource *res; int rid; sbintime_t byte_time_sbt; + int rb_pinctl_idx; + gpio_pin_t rb_sclpin; + gpio_pin_t rb_sdapin; }; static phandle_t i2c_get_node(device_t, device_t); @@ -274,6 +282,68 @@ i2c_error_handler(struct i2c_softc *sc, int error) } static int +i2c_recover_getsda(void *ctx) +{ + bool active; + + gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sdapin, &active); + return (active); +} + +static void +i2c_recover_setsda(void *ctx, int value) +{ + + gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sdapin, value); +} + +static int +i2c_recover_getscl(void *ctx) +{ + bool active; + + gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sclpin, &active); + return (active); + +} + +static void +i2c_recover_setscl(void *ctx, int value) +{ + + gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sclpin, value); +} + +static int +i2c_recover_bus(struct i2c_softc *sc) +{ + struct iicrb_pin_access pins; + int err; + + /* + * If we have gpio pinmux config, reconfigure the pins to gpio mode, + * invoke iic_recover_bus which checks for a hung bus and bitbangs a + * recovery sequence if necessary, then configure the pins back to i2c + * mode (idx 0). + */ + if (sc->rb_pinctl_idx == 0) + return (0); + + fdt_pinctrl_configure(sc->dev, sc->rb_pinctl_idx); + + pins.ctx = sc; + pins.getsda = i2c_recover_getsda; + pins.setsda = i2c_recover_setsda; + pins.getscl = i2c_recover_getscl; + pins.setscl = i2c_recover_setscl; + err = iic_recover_bus(&pins); + + fdt_pinctrl_configure(sc->dev, 0); + + return (err); +} + +static int i2c_probe(device_t dev) { @@ -291,7 +361,10 @@ i2c_probe(device_t dev) static int i2c_attach(device_t dev) { + char wrkstr[16]; struct i2c_softc *sc; + phandle_t node; + int err, cfgidx; sc = device_get_softc(dev); sc->dev = dev; @@ -310,6 +383,49 @@ i2c_attach(device_t dev) return (ENXIO); } + /* + * Set up for bus recovery using gpio pins, if the pinctrl and gpio + * properties are present. This is optional. If all the config data is + * not in place, we just don't do gpio bitbang bus recovery. + */ + node = ofw_bus_get_node(sc->dev); + + err = gpio_pin_get_by_ofw_property(dev, node, "scl-gpios", + &sc->rb_sclpin); + if (err != 0) + goto no_recovery; + err = gpio_pin_get_by_ofw_property(dev, node, "sda-gpios", + &sc->rb_sdapin); + if (err != 0) + goto no_recovery; + + /* + * Preset the gpio pins to output high (idle bus state). The signal + * won't actually appear on the pins until the bus recovery code changes + * the pinmux config from i2c to gpio. + */ + gpio_pin_setflags(sc->rb_sclpin, GPIO_PIN_OUTPUT); + gpio_pin_setflags(sc->rb_sdapin, GPIO_PIN_OUTPUT); + gpio_pin_set_active(sc->rb_sclpin, true); + gpio_pin_set_active(sc->rb_sdapin, true); + + /* + * Obtain the index of pinctrl node for bus recovery using gpio pins, + * then confirm that pinctrl properties exist for that index and for the + * default pinctrl-0. If sc->rb_pinctl_idx is non-zero, the reset code + * will also do a bus recovery, so setting this value must be last. + */ + err = ofw_bus_find_string_index(node, "pinctrl-names", "gpio", &cfgidx); + if (err == 0) { + snprintf(wrkstr, sizeof(wrkstr), "pinctrl-%d", cfgidx); + if (OF_hasprop(node, "pinctrl-0") && OF_hasprop(node, wrkstr)) + sc->rb_pinctl_idx = cfgidx; + } + +no_recovery: + + /* We don't do a hardware reset here because iicbus_attach() does it. */ + bus_generic_attach(dev); return (0); } @@ -339,7 +455,7 @@ i2c_repeated_start(device_t dev, u_char slave, int tim } static int -i2c_start(device_t dev, u_char slave, int timeout) +i2c_start_ll(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; @@ -361,6 +477,31 @@ i2c_start(device_t dev, u_char slave, int timeout) } static int +i2c_start(device_t dev, u_char slave, int timeout) +{ + struct i2c_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* + * Invoke the low-level code to put the bus into master mode and address + * the given slave. If that fails, idle the controller and attempt a + * bus recovery, and then try again one time. Signaling a start and + * addressing the slave is the only operation that a low-level driver + * can safely retry without any help from the upper layers that know + * more about the slave device. + */ + if ((error = i2c_start_ll(dev, slave, timeout)) != 0) { + i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); + if ((error = i2c_recover_bus(sc)) != 0) + return (error); + error = i2c_start_ll(dev, slave, timeout); + } + return (error); +} + +static int i2c_stop(device_t dev) { struct i2c_softc *sc; @@ -409,7 +550,12 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_c i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); i2c_write_reg(sc, I2C_FDR_REG, (uint8_t)clkdiv_table[i].regcode); - return (IIC_NOERR); + + /* + * Now that the controller is idle, perform bus recovery. If the bus + * isn't hung, this a fairly fast no-op. + */ + return (i2c_recover_bus(sc)); } static int