diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2015-09-01 09:43:53 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2015-09-01 09:43:53 +0200 |
commit | fae6eea0e6af8d9eb8ac02f1a4d6307ebc28fe5b (patch) | |
tree | 06b127d1456e93d49ef7342ae65bb47085bb9ce1 /drivers | |
parent | f387bc96bb23658ecdaa67bc9bff6fd290f636f8 (diff) | |
parent | ffcabbe125d31dcb4f25dbb8f29be7adcfcf408e (diff) | |
download | barebox-fae6eea0e6af8d9eb8ac02f1a4d6307ebc28fe5b.tar.gz barebox-fae6eea0e6af8d9eb8ac02f1a4d6307ebc28fe5b.tar.xz |
Merge branch 'for-next/gpio'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/eeprom/at24.c | 38 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-bit.c | 44 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-gpio.c | 10 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 88 | ||||
-rw-r--r-- | drivers/i2c/i2c.c | 130 |
5 files changed, 300 insertions, 10 deletions
diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c index bb2dd533b6..76f30e7e22 100644 --- a/drivers/eeprom/at24.c +++ b/drivers/eeprom/at24.c @@ -21,6 +21,8 @@ #include <linux/log2.h> #include <i2c/i2c.h> #include <i2c/at24.h> +#include <gpio.h> +#include <of_gpio.h> /* * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. @@ -55,6 +57,8 @@ struct at24_data { u8 *writebuf; unsigned write_max; unsigned num_addresses; + int wp_gpio; + int wp_active_low; /* * Some chips tie up multiple I2C addresses; dummy devices reserve @@ -345,6 +349,25 @@ static ssize_t at24_cdev_write(struct cdev *cdev, const void *buf, size_t count, return at24_write(at24, buf, off, count); } +static ssize_t at24_cdev_protect(struct cdev *cdev, size_t count, loff_t offset, + int prot) +{ + struct at24_data *at24 = cdev->priv; + + if (!gpio_is_valid(at24->wp_gpio)) + return -EOPNOTSUPP; + + prot = !!prot; + if (at24->wp_active_low) + prot = !prot; + + gpio_set_value(at24->wp_gpio, prot); + + udelay(50); + + return 0; +} + static int at24_probe(struct device_d *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -403,6 +426,7 @@ static int at24_probe(struct device_d *dev) at24->cdev.ops = &at24->fops; at24->fops.lseek = dev_lseek_default; at24->fops.read = at24_cdev_read, + at24->fops.protect = at24_cdev_protect, at24->cdev.size = chip.byte_len; writable = !(chip.flags & AT24_FLAG_READONLY); @@ -419,6 +443,19 @@ static int at24_probe(struct device_d *dev) at24->writebuf = xmalloc(write_max + 2); } + at24->wp_gpio = -1; + if (dev->device_node) { + enum of_gpio_flags flags; + at24->wp_gpio = of_get_named_gpio_flags(dev->device_node, + "wp-gpios", 0, &flags); + if (gpio_is_valid(at24->wp_gpio)) { + at24->wp_active_low = flags & OF_GPIO_ACTIVE_LOW; + gpio_request(at24->wp_gpio, "eeprom-wp"); + gpio_direction_output(at24->wp_gpio, + !at24->wp_active_low); + } + } + at24->client[0] = client; /* use dummy devices for multiple-address chips */ @@ -440,6 +477,7 @@ static int at24_probe(struct device_d *dev) return 0; err_clients: + gpio_free(at24->wp_gpio); kfree(at24->writebuf); kfree(at24); err_out: diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index dc43eb83a4..2563c0d25a 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -106,9 +106,10 @@ static int sclhi(struct i2c_algo_bit_data *adap) } } #ifdef DEBUG - if (jiffies != start && i2c_debug >= 3) - pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go " - "high\n", jiffies - start); + if ((get_time_ns() - start) < 10000) + pr_debug("i2c-algo-bit: needed %u usecs for SCL to go " + "high\n", (unsigned int)(get_time_ns() - start) / + 1000); #endif done: @@ -116,6 +117,31 @@ done: return 0; } +static int wait_busy(struct i2c_algo_bit_data *adap) +{ + uint64_t start; + + if (sclhi(adap) < 0) + return -ETIMEDOUT; + + start = get_time_ns(); + while (!getsda(adap)) { + if (is_timeout(start, adap->timeout_ms * MSECOND)) { + if (getsda(adap)) + break; + return -ETIMEDOUT; + } + } +#ifdef DEBUG + if ((get_time_ns() - start) < 10000) + pr_debug("i2c-algo-bit: needed %u usecs for SDA to go " + "high\n", (unsigned int)(get_time_ns() - start) / + 1000); +#endif + + udelay(adap->udelay); + return 0; +} /* --- other auxiliary functions -------------------------------------- */ static void i2c_start(struct i2c_algo_bit_data *adap) @@ -426,10 +452,7 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) count--; bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", - inval, - (flags & I2C_M_NO_RD_ACK) - ? "(no ack/nak)" - : (count ? "A" : "NA")); + inval, count ? "A" : "NA"); inval = acknak(i2c_adap, count); if (inval < 0) @@ -514,6 +537,13 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, return ret; } + if (wait_busy(adap) < 0) { /* timeout */ + dev_warn(&i2c_adap->dev, "timeout waiting for bus ready\n"); + ret = i2c_recover_bus(i2c_adap); + if (ret < 0) + return ret; + } + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); i2c_start(adap); for (i = 0; i < num; i++) { diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index b4a0ecdb20..9362ed181f 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -10,6 +10,7 @@ #include <common.h> #include <driver.h> +#include <malloc.h> #include <i2c/i2c.h> #include <i2c/i2c-algo-bit.h> #include <i2c/i2c-gpio.h> @@ -187,6 +188,13 @@ static int i2c_gpio_probe(struct device_d *dev) adap->algo_data = bit_data; adap->dev.parent = dev; adap->dev.device_node = dev->device_node; + adap->bus_recovery_info = xzalloc(sizeof(*adap->bus_recovery_info)); + adap->bus_recovery_info->scl_gpio = pdata->scl_pin; + adap->bus_recovery_info->sda_gpio = pdata->sda_pin; + adap->bus_recovery_info->get_sda = i2c_get_sda_gpio_value; + adap->bus_recovery_info->get_scl = i2c_get_scl_gpio_value; + adap->bus_recovery_info->set_scl = i2c_set_scl_gpio_value; + adap->bus_recovery_info->recover_bus = i2c_generic_scl_recovery; adap->nr = dev->id; ret = i2c_bit_add_numbered_bus(adap); @@ -201,10 +209,12 @@ static int i2c_gpio_probe(struct device_d *dev) return 0; err_add_bus: + free(adap->bus_recovery_info); gpio_free(pdata->scl_pin); err_request_scl: gpio_free(pdata->sda_pin); err_request_sda: + free(priv); return ret; } diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 330db98982..48c55daeda 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -107,16 +107,20 @@ #define OMAP_I2C_SCLH_HSSCLH 8 /* I2C System Test Register (OMAP_I2C_SYSTEST): */ -#ifdef DEBUG #define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ #define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */ #define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ #define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +/* Functional mode */ +#define OMAP_I2C_SYSTEST_SCL_I_FUNC (1 << 8) /* SCL line input value */ +#define OMAP_I2C_SYSTEST_SCL_O_FUNC (1 << 7) /* SCL line output value */ +#define OMAP_I2C_SYSTEST_SDA_I_FUNC (1 << 6) /* SDA line input value */ +#define OMAP_I2C_SYSTEST_SDA_O_FUNC (1 << 5) /* SDA line output value */ +/* SDA/SCL IO mode */ #define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */ #define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */ #define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */ #define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ -#endif /* OCP_SYSSTATUS bit definitions */ #define SYSS_RESETDONE_MASK (1 << 0) @@ -492,7 +496,7 @@ static int omap_i2c_wait_for_bb(struct i2c_adapter *adapter) while (omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { if (is_timeout(start, MSECOND)) { dev_warn(&adapter->dev, "timeout waiting for bus ready\n"); - return -ETIMEDOUT; + return i2c_recover_bus(adapter); } } @@ -673,6 +677,10 @@ omap_i2c_isr(struct omap_i2c_struct *dev) /* * ProDB0017052: Clear ARDY bit twice */ + if (stat & OMAP_I2C_STAT_ARDY) + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ARDY); + + if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY | @@ -986,6 +994,79 @@ out: #define OMAP_I2C_SCHEME_0 0 #define OMAP_I2C_SCHEME_1 1 +static int omap_i2c_get_scl(struct i2c_adapter *adapter) +{ + struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter); + u32 reg; + + reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC; +} + +static int omap_i2c_get_sda(struct i2c_adapter *adapter) +{ + struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter); + u32 reg; + + reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC; +} + +static void omap_i2c_set_scl(struct i2c_adapter *adapter, int val) +{ + struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter); + u32 reg; + + reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG); + if (val) + reg |= OMAP_I2C_SYSTEST_SCL_O; + else + reg &= ~OMAP_I2C_SYSTEST_SCL_O; + omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_prepare_recovery(struct i2c_adapter *adapter) +{ + struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter); + u32 reg; + + reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG); + /* enable test mode */ + reg |= OMAP_I2C_SYSTEST_ST_EN; + /* select SDA/SCL IO mode */ + reg |= 3 << OMAP_I2C_SYSTEST_TMODE_SHIFT; + /* set SCL to high-impedance state (reset value is 0) */ + reg |= OMAP_I2C_SYSTEST_SCL_O; + /* set SDA to high-impedance state (reset value is 0) */ + reg |= OMAP_I2C_SYSTEST_SDA_O; + omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_unprepare_recovery(struct i2c_adapter *adapter) +{ + struct omap_i2c_struct *i2c_omap = to_omap_i2c_struct(adapter); + u32 reg; + + reg = omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSTEST_REG); + /* restore reset values */ + reg &= ~OMAP_I2C_SYSTEST_ST_EN; + reg &= ~OMAP_I2C_SYSTEST_TMODE_MASK; + reg &= ~OMAP_I2C_SYSTEST_SCL_O; + reg &= ~OMAP_I2C_SYSTEST_SDA_O; + omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSTEST_REG, reg); +} + +static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = { + .get_scl = omap_i2c_get_scl, + .get_sda = omap_i2c_get_sda, + .set_scl = omap_i2c_set_scl, + .prepare_recovery = omap_i2c_prepare_recovery, + .unprepare_recovery = omap_i2c_unprepare_recovery, + .recover_bus = i2c_generic_scl_recovery, +}; + static int __init i2c_omap_probe(struct device_d *pdev) { @@ -1097,6 +1178,7 @@ i2c_omap_probe(struct device_d *pdev) i2c_omap->adapter.nr = pdev->id; i2c_omap->adapter.dev.parent = pdev; i2c_omap->adapter.dev.device_node = pdev->device_node; + i2c_omap->adapter.bus_recovery_info = &omap_i2c_bus_recovery_info; /* i2c device drivers may be active on return from add_adapter() */ r = i2c_add_numbered_adapter(&i2c_omap->adapter); diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index f0df666b97..52aaea8170 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -23,6 +23,7 @@ #include <xfuncs.h> #include <init.h> #include <of.h> +#include <gpio.h> #include <i2c/i2c.h> @@ -228,6 +229,135 @@ int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count) } EXPORT_SYMBOL(i2c_write_reg); +/* i2c bus recovery routines */ +int i2c_get_scl_gpio_value(struct i2c_adapter *adap) +{ + gpio_direction_input(adap->bus_recovery_info->scl_gpio); + return gpio_get_value(adap->bus_recovery_info->scl_gpio); +} + +void i2c_set_scl_gpio_value(struct i2c_adapter *adap, int val) +{ + if (val) + gpio_direction_input(adap->bus_recovery_info->scl_gpio); + else + gpio_direction_output(adap->bus_recovery_info->scl_gpio, 0); +} + +int i2c_get_sda_gpio_value(struct i2c_adapter *adap) +{ + return gpio_get_value(adap->bus_recovery_info->sda_gpio); +} + +static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + struct device_d *dev = &adap->dev; + int ret = 0; + + ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl"); + if (ret) { + dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio); + return ret; + } + + if (bri->get_sda) { + if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) { + /* work without SDA polling */ + dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n", + bri->sda_gpio); + bri->get_sda = NULL; + } + } + + return ret; +} + +static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + + if (bri->get_sda) + gpio_free(bri->sda_gpio); + + gpio_free(bri->scl_gpio); +} + +/* + * We are generating clock pulses. ndelay() determines durating of clk pulses. + * We will generate clock with rate 100 KHz and so duration of both clock levels + * is: delay in ns = (10^6 / 100) / 2 + */ +#define RECOVERY_NDELAY 5000 +#define RECOVERY_CLK_CNT 9 + +static int i2c_generic_recovery(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + int i = 0, val = 1, ret = 0; + + if (bri->prepare_recovery) + bri->prepare_recovery(adap); + + bri->set_scl(adap, val); + ndelay(RECOVERY_NDELAY); + + /* + * By this time SCL is high, as we need to give 9 falling-rising edges + */ + while (i++ < RECOVERY_CLK_CNT * 2) { + if (val) { + /* Break if SDA is high */ + if (bri->get_sda && bri->get_sda(adap)) + break; + /* SCL shouldn't be low here */ + if (!bri->get_scl(adap)) { + dev_err(&adap->dev, + "SCL is stuck low, exit recovery\n"); + ret = -EBUSY; + break; + } + } + + val = !val; + bri->set_scl(adap, val); + ndelay(RECOVERY_NDELAY); + } + + if (bri->unprepare_recovery) + bri->unprepare_recovery(adap); + + return ret; +} + +int i2c_generic_scl_recovery(struct i2c_adapter *adap) +{ + return i2c_generic_recovery(adap); +} + +int i2c_generic_gpio_recovery(struct i2c_adapter *adap) +{ + int ret; + + ret = i2c_get_gpios_for_recovery(adap); + if (ret) + return ret; + + ret = i2c_generic_recovery(adap); + i2c_put_gpios_for_recovery(adap); + + return ret; +} + +int i2c_recover_bus(struct i2c_adapter *adap) +{ + if (!adap->bus_recovery_info) + return -EOPNOTSUPP; + + dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); + return adap->bus_recovery_info->recover_bus(adap); +} + /** * i2c_new_device - instantiate one new I2C device * |