diff --git a/arch/arm/boards/phytec-phycore-pxa270/board.c b/arch/arm/boards/phytec-phycore-pxa270/board.c index 833c4c8..1424c9c 100644 --- a/arch/arm/boards/phytec-phycore-pxa270/board.c +++ b/arch/arm/boards/phytec-phycore-pxa270/board.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/include/asm/gpio.h b/arch/arm/include/asm/gpio.h deleted file mode 100644 index b3c1efe..0000000 --- a/arch/arm/include/asm/gpio.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _ARCH_ARM_GPIO_H -#define _ARCH_ARM_GPIO_H - -#ifndef CONFIG_GPIOLIB -#include -#else -#include -#endif - -#endif /* _ARCH_ARM_GPIO_H */ diff --git a/arch/arm/mach-at91/include/mach/gpio.h b/arch/arm/mach-at91/include/mach/gpio.h index 4e9d686..bdc0cb6 100644 --- a/arch/arm/mach-at91/include/mach/gpio.h +++ b/arch/arm/mach-at91/include/mach/gpio.h @@ -7,8 +7,6 @@ #ifndef __AT91_GPIO_H__ #define __AT91_GPIO_H__ -#include - #define MAX_NB_GPIO_PER_BANK 32 static inline unsigned pin_to_bank(unsigned pin) diff --git a/arch/arm/mach-ep93xx/include/mach/gpio.h b/arch/arm/mach-ep93xx/include/mach/gpio.h deleted file mode 100644 index 306ab4c..0000000 --- a/arch/arm/mach-ep93xx/include/mach/gpio.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/arm/mach-mxs/include/mach/gpio.h b/arch/arm/mach-mxs/include/mach/gpio.h deleted file mode 100644 index 8643c98..0000000 --- a/arch/arm/mach-mxs/include/mach/gpio.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * (C) Copyright 2010 Juergen Beisert - Pengutronix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __ASM_MACH_GPIO_H -#define __ASM_MACH_GPIO_H - -#include - -#endif /* __ASM_MACH_GPIO_H */ diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c index 4e98493..7dd6ac0 100644 --- a/arch/arm/mach-pxa/gpio.c +++ b/arch/arm/mach-pxa/gpio.c @@ -66,3 +66,35 @@ return 0; } + +int gpio_get_value(unsigned gpio) +{ + return GPLR(gpio) & GPIO_bit(gpio); +} + +void gpio_set_value(unsigned gpio, int value) +{ + if (value) + GPSR(gpio) = GPIO_bit(gpio); + else + GPCR(gpio) = GPIO_bit(gpio); +} + +int gpio_direction_input(unsigned gpio) +{ + if (__gpio_is_inverted(gpio)) + GPDR(gpio) |= GPIO_bit(gpio); + else + GPDR(gpio) &= ~GPIO_bit(gpio); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + gpio_set_value(gpio, value); + if (__gpio_is_inverted(gpio)) + GPDR(gpio) &= ~GPIO_bit(gpio); + else + GPDR(gpio) |= GPIO_bit(gpio); + return 0; +} diff --git a/arch/arm/mach-pxa/include/plat/gpio.h b/arch/arm/mach-pxa/include/plat/gpio.h index 4c7b526..35f9071 100644 --- a/arch/arm/mach-pxa/include/plat/gpio.h +++ b/arch/arm/mach-pxa/include/plat/gpio.h @@ -31,38 +31,6 @@ #define GFER_OFFSET 0x3C #define GEDR_OFFSET 0x48 -static inline int gpio_get_value(unsigned gpio) -{ - return GPLR(gpio) & GPIO_bit(gpio); -} - -static inline void gpio_set_value(unsigned gpio, int value) -{ - if (value) - GPSR(gpio) = GPIO_bit(gpio); - else - GPCR(gpio) = GPIO_bit(gpio); -} - -static inline int gpio_direction_input(unsigned gpio) -{ - if (__gpio_is_inverted(gpio)) - GPDR(gpio) |= GPIO_bit(gpio); - else - GPDR(gpio) &= ~GPIO_bit(gpio); - return 0; -} - -static inline int gpio_direction_output(unsigned gpio, int value) -{ - gpio_set_value(gpio, value); - if (__gpio_is_inverted(gpio)) - GPDR(gpio) &= ~GPIO_bit(gpio); - else - GPDR(gpio) |= GPIO_bit(gpio); - return 0; -} - /* NOTE: some PXAs have fewer on-chip GPIOs (like PXA255, with 85). * Those cases currently cause holes in the GPIO number space, the * actual number of the last GPIO is recorded by 'pxa_last_gpio'. diff --git a/arch/arm/mach-samsung/gpio-s3c24x0.c b/arch/arm/mach-samsung/gpio-s3c24x0.c index f62588f..58ca284 100644 --- a/arch/arm/mach-samsung/gpio-s3c24x0.c +++ b/arch/arm/mach-samsung/gpio-s3c24x0.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-samsung/include/mach/gpio.h b/arch/arm/mach-samsung/include/mach/gpio.h deleted file mode 100644 index 3920667..0000000 --- a/arch/arm/mach-samsung/include/mach/gpio.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __ASM_MACH_GPIO_H -#define __ASM_MACH_GPIO_H - -#include - -#endif /* __ASM_MACH_GPIO_H */ diff --git a/arch/mips/include/asm/gpio.h b/arch/mips/include/asm/gpio.h deleted file mode 100644 index 41a9589..0000000 --- a/arch/mips/include/asm/gpio.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ARCH_MIPS_GPIO_H -#define _ARCH_MIPS_GPIO_H - -#include - -#endif /* _ARCH_MIPS_GPIO_H */ diff --git a/arch/ppc/boards/freescale-p1010rdb/p1010rdb.c b/arch/ppc/boards/freescale-p1010rdb/p1010rdb.c index 5bae584..60cae46 100644 --- a/arch/ppc/boards/freescale-p1010rdb/p1010rdb.c +++ b/arch/ppc/boards/freescale-p1010rdb/p1010rdb.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/arch/ppc/boards/geip-da923rc/da923rc.c b/arch/ppc/boards/geip-da923rc/da923rc.c index 3d77349..b6b5d56 100644 --- a/arch/ppc/boards/geip-da923rc/da923rc.c +++ b/arch/ppc/boards/geip-da923rc/da923rc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/ppc/mach-mpc85xx/include/mach/gpio.h b/arch/ppc/mach-mpc85xx/include/mach/gpio.h index 61f6349..b41ecc5 100644 --- a/arch/ppc/mach-mpc85xx/include/mach/gpio.h +++ b/arch/ppc/mach-mpc85xx/include/mach/gpio.h @@ -10,8 +10,6 @@ #ifndef _MACH_PPC_GPIO_H #define _MACH_PPC_GPIO_H -#include - extern void fsl_enable_gpiout(void); #endif /* _MACH_PPC_GPIO_H */ diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c index bb2dd53..76f30e7 100644 --- a/drivers/eeprom/at24.c +++ b/drivers/eeprom/at24.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include /* * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. @@ -55,6 +57,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 dc43eb8..2563c0d 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -106,9 +106,10 @@ } } #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 @@ 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 @@ 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 @@ 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 b4a0ecd..9362ed1 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -187,6 +188,13 @@ 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 @@ 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 330db98..48c55da 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 @@ 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 @@ /* * 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 @@ #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->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 f0df666..52aaea8 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -228,6 +229,135 @@ } 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 * diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h deleted file mode 100644 index 7674970..0000000 --- a/include/asm-generic/gpio.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __ASM_GENERIC_GPIO_H -#define __ASM_GENERIC_GPIO_H - -void gpio_set_value(unsigned gpio, int value); -int gpio_get_value(unsigned gpio); -int gpio_direction_output(unsigned gpio, int value); -int gpio_direction_input(unsigned gpio); - -#endif /* __ASM_GENERIC_GPIO_H */ diff --git a/include/gpio.h b/include/gpio.h index f116ea6..7b3f512 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -1,7 +1,28 @@ #ifndef __GPIO_H #define __GPIO_H -#include +#ifdef CONFIG_GENERIC_GPIO +void gpio_set_value(unsigned gpio, int value); +int gpio_get_value(unsigned gpio); +int gpio_direction_output(unsigned gpio, int value); +int gpio_direction_input(unsigned gpio); +#else +static inline void gpio_set_value(unsigned gpio, int value) +{ +} +static inline int gpio_get_value(unsigned gpio) +{ + return 0; +} +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return -EINVAL; +} +static inline int gpio_direction_input(unsigned gpio) +{ + return -EINVAL; +} +#endif #define ARCH_NR_GPIOS 256 diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index 4696f43..12e4827 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -19,6 +19,8 @@ #include #include +struct i2c_adapter; + /* * struct i2c_platform_data - structure of platform data for MXC I2C driver * @param bitrate Bus speed measured in Hz @@ -61,6 +63,47 @@ __u16 len; /**< Number of data bytes in @buf being read from or written to the I2C slave address. */ }; +/** + * struct i2c_bus_recovery_info - I2C bus recovery information + * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or + * i2c_generic_scl_recovery() or i2c_generic_gpio_recovery(). + * @get_scl: This gets current value of SCL line. Mandatory for generic SCL + * recovery. Used internally for generic GPIO recovery. + * @set_scl: This sets/clears SCL line. Mandatory for generic SCL recovery. Used + * internally for generic GPIO recovery. + * @get_sda: This gets current value of SDA line. Optional for generic SCL + * recovery. Used internally, if sda_gpio is a valid GPIO, for generic GPIO + * recovery. + * @prepare_recovery: This will be called before starting recovery. Platform may + * configure padmux here for SDA/SCL line or something else they want. + * @unprepare_recovery: This will be called after completing recovery. Platform + * may configure padmux here for SDA/SCL line or something else they want. + * @scl_gpio: gpio number of the SCL line. Only required for GPIO recovery. + * @sda_gpio: gpio number of the SDA line. Only required for GPIO recovery. + */ +struct i2c_bus_recovery_info { + int (*recover_bus)(struct i2c_adapter *); + + int (*get_scl)(struct i2c_adapter *); + void (*set_scl)(struct i2c_adapter *, int val); + int (*get_sda)(struct i2c_adapter *); + + void (*prepare_recovery)(struct i2c_adapter *); + void (*unprepare_recovery)(struct i2c_adapter *); + + /* gpio recovery */ + int scl_gpio; + int sda_gpio; +}; + +int i2c_recover_bus(struct i2c_adapter *adap); + +/* Generic recovery routines */ +int i2c_get_scl_gpio_value(struct i2c_adapter *adap); +void i2c_set_scl_gpio_value(struct i2c_adapter *adap, int val); +int i2c_get_sda_gpio_value(struct i2c_adapter *adap); +int i2c_generic_gpio_recovery(struct i2c_adapter *adap); +int i2c_generic_scl_recovery(struct i2c_adapter *adap); /** * i2c_adapter is the structure used to identify a physical i2c bus @@ -74,6 +117,8 @@ struct list_head list; int retries; void *algo_data; + + struct i2c_bus_recovery_info *bus_recovery_info; };