diff options
Diffstat (limited to 'drivers/i2c')
| -rw-r--r-- | drivers/i2c/busses/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/i2c/busses/Makefile | 2 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-bcm2708.c | 510 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-bcm2835.c | 133 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-designware-common.c | 39 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-designware-core.h | 10 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-designware-master.c | 76 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-gpio.c | 4 | ||||
| -rw-r--r-- | drivers/i2c/i2c-mux.c | 5 |
9 files changed, 786 insertions, 12 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 070d014fdc5d..15c544cece03 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -16,6 +16,25 @@ config I2C_CCGX_UCSI for Cypress CCGx Type-C controller. Individual bus drivers need to select this one on demand. +config I2C_BCM2708 + tristate "BCM2708 BSC" + depends on ARCH_BCM2835 + help + Enabling this option will add BSC (Broadcom Serial Controller) + support for the BCM2708. BSC is a Broadcom proprietary bus compatible + with I2C/TWI/SMBus. + +config I2C_BCM2708_BAUDRATE + prompt "BCM2708 I2C baudrate" + depends on I2C_BCM2708 + int + default 100000 + help + Set the I2C baudrate. This will alter the default value. A + different baudrate can be set by using a module parameter as well. If + no parameter is provided when loading, this is the value that will be + used. + config I2C_ALI1535 tristate "ALI 1535" depends on PCI && HAS_IOPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 04db855fdfd6..d14b7b747d8d 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -3,6 +3,8 @@ # Makefile for the i2c bus drivers. # +obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o + # ACPI drivers obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c new file mode 100644 index 000000000000..09907b5c2540 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm2708.c @@ -0,0 +1,510 @@ +/* + * Driver for Broadcom BCM2708 BSC Controllers + * + * Copyright (C) 2012 Chris Boot & Frank Buss + * + * This driver is inspired by: + * i2c-ocores.c, by Peter Korsgaard <[email protected]> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/wait.h> + +/* BSC register offsets */ +#define BSC_C 0x00 +#define BSC_S 0x04 +#define BSC_DLEN 0x08 +#define BSC_A 0x0c +#define BSC_FIFO 0x10 +#define BSC_DIV 0x14 +#define BSC_DEL 0x18 +#define BSC_CLKT 0x1c + +/* Bitfields in BSC_C */ +#define BSC_C_I2CEN 0x00008000 +#define BSC_C_INTR 0x00000400 +#define BSC_C_INTT 0x00000200 +#define BSC_C_INTD 0x00000100 +#define BSC_C_ST 0x00000080 +#define BSC_C_CLEAR_1 0x00000020 +#define BSC_C_CLEAR_2 0x00000010 +#define BSC_C_READ 0x00000001 + +/* Bitfields in BSC_S */ +#define BSC_S_CLKT 0x00000200 +#define BSC_S_ERR 0x00000100 +#define BSC_S_RXF 0x00000080 +#define BSC_S_TXE 0x00000040 +#define BSC_S_RXD 0x00000020 +#define BSC_S_TXD 0x00000010 +#define BSC_S_RXR 0x00000008 +#define BSC_S_TXW 0x00000004 +#define BSC_S_DONE 0x00000002 +#define BSC_S_TA 0x00000001 + +#define I2C_WAIT_LOOP_COUNT 200 + +#define DRV_NAME "bcm2708_i2c" + +static unsigned int baudrate; +module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +MODULE_PARM_DESC(baudrate, "The I2C baudrate"); + +static bool combined = false; +module_param(combined, bool, 0644); +MODULE_PARM_DESC(combined, "Use combined transactions"); + +struct bcm2708_i2c { + struct i2c_adapter adapter; + + spinlock_t lock; + void __iomem *base; + int irq; + struct clk *clk; + u32 cdiv; + u32 clk_tout; + + struct completion done; + + struct i2c_msg *msg; + int pos; + int nmsgs; + bool error; +}; + +static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg) +{ + return readl(bi->base + reg); +} + +static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val) +{ + writel(val, bi->base + reg); +} + +static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi) +{ + bcm2708_wr(bi, BSC_C, 0); + bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); +} + +static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi) +{ + while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_RXD)) + bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); +} + +static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) +{ + while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_TXD)) + bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); +} + +static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi) +{ + u32 cdiv, s, clk_tout; + u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; + int wait_loops = I2C_WAIT_LOOP_COUNT; + + /* Can't call clk_get_rate as it locks a mutex and here we are spinlocked. + * Use the value that we cached in the probe. + */ + cdiv = bi->cdiv; + clk_tout = bi->clk_tout; + + if (bi->msg->flags & I2C_M_RD) + c |= BSC_C_INTR | BSC_C_READ; + else + c |= BSC_C_INTT; + + bcm2708_wr(bi, BSC_CLKT, clk_tout); + bcm2708_wr(bi, BSC_DIV, cdiv); + bcm2708_wr(bi, BSC_A, bi->msg->addr); + bcm2708_wr(bi, BSC_DLEN, bi->msg->len); + if (combined) + { + /* Do the next two messages meet combined transaction criteria? + - Current message is a write, next message is a read + - Both messages to same slave address + - Write message can fit inside FIFO (16 bytes or less) */ + if ( (bi->nmsgs > 1) && + !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && + (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { + + /* Clear FIFO */ + bcm2708_wr(bi, BSC_C, BSC_C_CLEAR_1); + + /* Fill FIFO with entire write message (16 byte FIFO) */ + while (bi->pos < bi->msg->len) { + bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); + } + /* Start write transfer (no interrupts, don't clear FIFO) */ + bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); + + /* poll for transfer start bit (should only take 1-20 polls) */ + do { + s = bcm2708_rd(bi, BSC_S); + } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)) && --wait_loops >= 0); + + /* did we time out or some error occured? */ + if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) { + return -1; + } + + /* Send next read message before the write transfer finishes. */ + bi->nmsgs--; + bi->msg++; + bi->pos = 0; + bcm2708_wr(bi, BSC_DLEN, bi->msg->len); + c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; + } + } + bcm2708_wr(bi, BSC_C, c); + + return 0; +} + +static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) +{ + struct bcm2708_i2c *bi = dev_id; + bool handled = true; + u32 s; + int ret; + + spin_lock(&bi->lock); + + /* we may see camera interrupts on the "other" I2C channel + Just return if we've not sent anything */ + if (!bi->nmsgs || !bi->msg) { + goto early_exit; + } + + s = bcm2708_rd(bi, BSC_S); + + if (s & (BSC_S_CLKT | BSC_S_ERR)) { + bcm2708_bsc_reset(bi); + bi->error = true; + + bi->msg = 0; /* to inform the that all work is done */ + bi->nmsgs = 0; + /* wake up our bh */ + complete(&bi->done); + } else if (s & BSC_S_DONE) { + bi->nmsgs--; + + if (bi->msg->flags & I2C_M_RD) { + bcm2708_bsc_fifo_drain(bi); + } + + bcm2708_bsc_reset(bi); + + if (bi->nmsgs) { + /* advance to next message */ + bi->msg++; + bi->pos = 0; + ret = bcm2708_bsc_setup(bi); + if (ret < 0) { + bcm2708_bsc_reset(bi); + bi->error = true; + bi->msg = 0; /* to inform the that all work is done */ + bi->nmsgs = 0; + /* wake up our bh */ + complete(&bi->done); + goto early_exit; + } + } else { + bi->msg = 0; /* to inform the that all work is done */ + bi->nmsgs = 0; + /* wake up our bh */ + complete(&bi->done); + } + } else if (s & BSC_S_TXW) { + bcm2708_bsc_fifo_fill(bi); + } else if (s & BSC_S_RXR) { + bcm2708_bsc_fifo_drain(bi); + } else { + handled = false; + } + +early_exit: + spin_unlock(&bi->lock); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct bcm2708_i2c *bi = adap->algo_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&bi->lock, flags); + + reinit_completion(&bi->done); + bi->msg = msgs; + bi->pos = 0; + bi->nmsgs = num; + bi->error = false; + + ret = bcm2708_bsc_setup(bi); + + spin_unlock_irqrestore(&bi->lock, flags); + + /* check the result of the setup */ + if (ret < 0) + { + dev_err(&adap->dev, "transfer setup timed out\n"); + goto error_timeout; + } + + ret = wait_for_completion_timeout(&bi->done, adap->timeout); + if (ret == 0) { + dev_err(&adap->dev, "transfer timed out\n"); + goto error_timeout; + } + + ret = bi->error ? -EIO : num; + return ret; + +error_timeout: + spin_lock_irqsave(&bi->lock, flags); + bcm2708_bsc_reset(bi); + bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */ + bi->nmsgs = 0; + spin_unlock_irqrestore(&bi->lock, flags); + return -ETIMEDOUT; +} + +static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm bcm2708_i2c_algorithm = { + .master_xfer = bcm2708_i2c_master_xfer, + .functionality = bcm2708_i2c_functionality, +}; + +static int bcm2708_i2c_probe(struct platform_device *pdev) +{ + struct resource *regs; + int irq, err = -ENOMEM; + struct clk *clk; + struct bcm2708_i2c *bi; + struct i2c_adapter *adap; + unsigned long bus_hz; + u32 cdiv, clk_tout; + u32 baud; + + baud = CONFIG_I2C_BCM2708_BAUDRATE; + + if (pdev->dev.of_node) { + u32 bus_clk_rate; + pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); + if (pdev->id < 0) { + dev_err(&pdev->dev, "alias is missing\n"); + return -EINVAL; + } + if (!of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &bus_clk_rate)) + baud = bus_clk_rate; + else + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + } + + if (baudrate) + baud = baudrate; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "could not get IO memory\n"); + return -ENXIO; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get IRQ\n"); + return irq; + } + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + err = clk_prepare_enable(clk); + if (err) { + dev_err(&pdev->dev, "could not enable clk: %d\n", err); + goto out_clk_put; + } + + bi = kzalloc(sizeof(*bi), GFP_KERNEL); + if (!bi) + goto out_clk_disable; + + platform_set_drvdata(pdev, bi); + + adap = &bi->adapter; + adap->class = I2C_CLASS_HWMON; + adap->algo = &bcm2708_i2c_algorithm; + adap->algo_data = bi; + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + strscpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); + adap->dev.of_node = pdev->dev.of_node; + + switch (pdev->id) { + case 0: + adap->class = I2C_CLASS_HWMON; + break; + case 1: + adap->class = I2C_CLASS_HWMON; + break; + case 2: + adap->class = I2C_CLASS_HWMON; + break; + default: + dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\n"); + err = -ENXIO; + goto out_free_bi; + } + + spin_lock_init(&bi->lock); + init_completion(&bi->done); + + bi->base = ioremap(regs->start, resource_size(regs)); + if (!bi->base) { + dev_err(&pdev->dev, "could not remap memory\n"); + goto out_free_bi; + } + + bi->irq = irq; + bi->clk = clk; + + err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED, + dev_name(&pdev->dev), bi); + if (err) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", err); + goto out_iounmap; + } + + bcm2708_bsc_reset(bi); + + err = i2c_add_numbered_adapter(adap); + if (err < 0) { + dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err); + goto out_free_irq; + } + + bus_hz = clk_get_rate(bi->clk); + cdiv = bus_hz / baud; + if (cdiv > 0xffff) { + cdiv = 0xffff; + baud = bus_hz / cdiv; + } + + clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs. + if (clk_tout > 0xffff) + clk_tout = 0xffff; + + bi->cdiv = cdiv; + bi->clk_tout = clk_tout; + + dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", + pdev->id, (unsigned long)regs->start, irq, baud); + + return 0; + +out_free_irq: + free_irq(bi->irq, bi); +out_iounmap: + iounmap(bi->base); +out_free_bi: + kfree(bi); +out_clk_disable: + clk_disable_unprepare(clk); +out_clk_put: + clk_put(clk); + return err; +} + +static void bcm2708_i2c_remove(struct platform_device *pdev) +{ + struct bcm2708_i2c *bi = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + i2c_del_adapter(&bi->adapter); + free_irq(bi->irq, bi); + iounmap(bi->base); + clk_disable_unprepare(bi->clk); + clk_put(bi->clk); + kfree(bi); +} + +static const struct of_device_id bcm2708_i2c_of_match[] = { + { .compatible = "brcm,bcm2708-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); + +static struct platform_driver bcm2708_i2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = bcm2708_i2c_of_match, + }, + .probe = bcm2708_i2c_probe, + .remove = bcm2708_i2c_remove, +}; + +// module_platform_driver(bcm2708_i2c_driver); + + +static int __init bcm2708_i2c_init(void) +{ + return platform_driver_register(&bcm2708_i2c_driver); +} + +static void __exit bcm2708_i2c_exit(void) +{ + platform_driver_unregister(&bcm2708_i2c_driver); +} + +module_init(bcm2708_i2c_init); +module_exit(bcm2708_i2c_exit); + + + +MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708"); +MODULE_AUTHOR("Chris Boot <[email protected]>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 8554e790f8e3..cccb03eceafe 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -56,6 +56,22 @@ #define BCM2835_I2C_CDIV_MIN 0x0002 #define BCM2835_I2C_CDIV_MAX 0xFFFE +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "1=err, 2=isr, 3=xfer"); + +static unsigned int clk_tout_ms = 35; /* SMBUs-recommended 35ms */ +module_param(clk_tout_ms, uint, 0644); +MODULE_PARM_DESC(clk_tout_ms, "clock-stretch timeout (mS)"); + +#define BCM2835_DEBUG_MAX 512 +struct bcm2835_debug { + struct i2c_msg *msg; + int msg_idx; + size_t remain; + u32 status; +}; + struct bcm2835_i2c_dev { struct device *dev; void __iomem *regs; @@ -68,8 +84,78 @@ struct bcm2835_i2c_dev { u32 msg_err; u8 *msg_buf; size_t msg_buf_remaining; + struct bcm2835_debug debug[BCM2835_DEBUG_MAX]; + unsigned int debug_num; + unsigned int debug_num_msgs; }; +static inline void bcm2835_debug_add(struct bcm2835_i2c_dev *i2c_dev, u32 s) +{ + if (!i2c_dev->debug_num_msgs || i2c_dev->debug_num >= BCM2835_DEBUG_MAX) + return; + + i2c_dev->debug[i2c_dev->debug_num].msg = i2c_dev->curr_msg; + i2c_dev->debug[i2c_dev->debug_num].msg_idx = + i2c_dev->debug_num_msgs - i2c_dev->num_msgs; + i2c_dev->debug[i2c_dev->debug_num].remain = i2c_dev->msg_buf_remaining; + i2c_dev->debug[i2c_dev->debug_num].status = s; + i2c_dev->debug_num++; +} + +static void bcm2835_debug_print_status(struct bcm2835_i2c_dev *i2c_dev, + struct bcm2835_debug *d) +{ + u32 s = d->status; + + pr_info("isr: remain=%zu, status=0x%x : %s%s%s%s%s%s%s%s%s%s [i2c%d]\n", + d->remain, s, + s & BCM2835_I2C_S_TA ? "TA " : "", + s & BCM2835_I2C_S_DONE ? "DONE " : "", + s & BCM2835_I2C_S_TXW ? "TXW " : "", + s & BCM2835_I2C_S_RXR ? "RXR " : "", + s & BCM2835_I2C_S_TXD ? "TXD " : "", + s & BCM2835_I2C_S_RXD ? "RXD " : "", + s & BCM2835_I2C_S_TXE ? "TXE " : "", + s & BCM2835_I2C_S_RXF ? "RXF " : "", + s & BCM2835_I2C_S_ERR ? "ERR " : "", + s & BCM2835_I2C_S_CLKT ? "CLKT " : "", + i2c_dev->adapter.nr); +} + +static void bcm2835_debug_print_msg(struct bcm2835_i2c_dev *i2c_dev, + struct i2c_msg *msg, int i, int total, + const char *fname) +{ + pr_info("%s: msg(%d/%d) %s addr=0x%02x, len=%u flags=%s%s%s%s%s%s%s [i2c%d]\n", + fname, i, total, + msg->flags & I2C_M_RD ? "read" : "write", msg->addr, msg->len, + msg->flags & I2C_M_TEN ? "TEN" : "", + msg->flags & I2C_M_RECV_LEN ? "RECV_LEN" : "", + msg->flags & I2C_M_NO_RD_ACK ? "NO_RD_ACK" : "", + msg->flags & I2C_M_IGNORE_NAK ? "IGNORE_NAK" : "", + msg->flags & I2C_M_REV_DIR_ADDR ? "REV_DIR_ADDR" : "", + msg->flags & I2C_M_NOSTART ? "NOSTART" : "", + msg->flags & I2C_M_STOP ? "STOP" : "", + i2c_dev->adapter.nr); +} + +static void bcm2835_debug_print(struct bcm2835_i2c_dev *i2c_dev) +{ + struct bcm2835_debug *d; + unsigned int i; + + for (i = 0; i < i2c_dev->debug_num; i++) { + d = &i2c_dev->debug[i]; + if (d->status == ~0) + bcm2835_debug_print_msg(i2c_dev, d->msg, d->msg_idx, + i2c_dev->debug_num_msgs, "start_transfer"); + else + bcm2835_debug_print_status(i2c_dev, d); + } + if (i2c_dev->debug_num >= BCM2835_DEBUG_MAX) + pr_info("BCM2835_DEBUG_MAX reached\n"); +} + static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, u32 reg, u32 val) { @@ -111,6 +197,7 @@ static int clk_bcm2835_i2c_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw); u32 redl, fedl; + u32 clk_tout; u32 divider = clk_bcm2835_i2c_calc_divider(rate, parent_rate); if (divider == -EINVAL) @@ -134,6 +221,17 @@ static int clk_bcm2835_i2c_set_rate(struct clk_hw *hw, unsigned long rate, bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DEL, (fedl << BCM2835_I2C_FEDL_SHIFT) | (redl << BCM2835_I2C_REDL_SHIFT)); + + /* + * Set the clock stretch timeout. + */ + if (rate > 0xffff*1000/clk_tout_ms) + clk_tout = 0xffff; + else + clk_tout = clk_tout_ms*rate/1000; + + bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_CLKT, clk_tout); + return 0; } @@ -257,6 +355,7 @@ static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev) bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); + bcm2835_debug_add(i2c_dev, ~0); } static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev) @@ -283,12 +382,11 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) u32 val, err; val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + bcm2835_debug_add(i2c_dev, val); err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); - if (err) { + if (err && !(val & BCM2835_I2C_S_TA)) i2c_dev->msg_err = err; - goto complete; - } if (val & BCM2835_I2C_S_DONE) { if (!i2c_dev->curr_msg) { @@ -300,8 +398,6 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) if ((val & BCM2835_I2C_S_RXD) || i2c_dev->msg_buf_remaining) i2c_dev->msg_err = BCM2835_I2C_S_LEN; - else - i2c_dev->msg_err = 0; goto complete; } @@ -347,17 +443,29 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], { struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); unsigned long time_left; + bool ignore_nak = false; int i; - for (i = 0; i < (num - 1); i++) + if (debug) + i2c_dev->debug_num_msgs = num; + + if (debug > 2) + for (i = 0; i < num; i++) + bcm2835_debug_print_msg(i2c_dev, &msgs[i], i + 1, num, __func__); + + for (i = 0; i < (num - 1); i++) { if (msgs[i].flags & I2C_M_RD) { dev_warn_once(i2c_dev->dev, "only one read message supported, has to be last\n"); return -EOPNOTSUPP; } + if (msgs[i].flags & I2C_M_IGNORE_NAK) + ignore_nak = true; + } i2c_dev->curr_msg = msgs; i2c_dev->num_msgs = num; + i2c_dev->msg_err = 0; reinit_completion(&i2c_dev->completion); bcm2835_i2c_start_transfer(i2c_dev); @@ -367,6 +475,13 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], bcm2835_i2c_finish_transfer(i2c_dev); + if (ignore_nak) + i2c_dev->msg_err &= ~BCM2835_I2C_S_ERR; + + if (debug > 1 || (debug && (!time_left || i2c_dev->msg_err))) + bcm2835_debug_print(i2c_dev); + i2c_dev->debug_num_msgs = 0; + i2c_dev->debug_num = 0; if (!time_left) { bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); @@ -376,7 +491,9 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (!i2c_dev->msg_err) return num; - dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err); + if (debug) + dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", + i2c_dev->msg_err); if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) return -EREMOTEIO; @@ -386,7 +503,7 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], static u32 bcm2835_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; } static const struct i2c_algorithm bcm2835_i2c_algo = { diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 5b1e8f74c4ac..b19c1144b58e 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -63,6 +63,8 @@ static const char *const abort_sources[] = { "slave lost the bus while transmitting data to a remote master", [ABRT_SLAVE_RD_INTX] = "incorrect slave-transmitter mode configuration", + [ABRT_SLAVE_SDA_STUCK_AT_LOW] = + "SDA stuck at low", }; static int dw_reg_read(void *context, unsigned int reg, unsigned int *val) @@ -362,6 +364,9 @@ static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) { u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev); struct i2c_timings *t = &dev->timings; + u32 wanted_speed; + u32 legal_speed = 0; + int i; /* * Find bus speed from the "clock-frequency" device property, ACPI @@ -373,6 +378,30 @@ static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); else t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ; + + wanted_speed = t->bus_freq_hz; + + /* For unsupported speeds, scale down the lowest speed which is faster. */ + for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) { + /* supported speeds is in decreasing order */ + if (wanted_speed == supported_speeds[i]) { + legal_speed = 0; + break; + } + if (wanted_speed > supported_speeds[i]) + break; + + legal_speed = supported_speeds[i]; + } + + if (legal_speed) { + /* + * Pretend this was the requested speed, but preserve the preferred + * speed so the clock counts can be scaled. + */ + t->bus_freq_hz = legal_speed; + dev->wanted_bus_speed = wanted_speed; + } } int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev) @@ -652,8 +681,16 @@ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) { unsigned long abort_source = dev->abort_source; + unsigned int reg; int i; + if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) { + regmap_write(dev->map, DW_IC_ENABLE, + DW_IC_ENABLE_ENABLE | DW_IC_ENABLE_BUS_RECOVERY); + regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, reg, + !(reg & DW_IC_ENABLE_BUS_RECOVERY), + 1100, 200000); + } if (abort_source & DW_IC_TX_ABRT_NOACK) { for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) dev_dbg(dev->dev, @@ -668,6 +705,8 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) return -EAGAIN; if (abort_source & DW_IC_TX_ABRT_GCALL_READ) return -EINVAL; /* wrong msgs[] data */ + if (abort_source & DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW) + return -EREMOTEIO; return -EIO; } diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 347843b4f5dd..2d11299580bf 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -78,9 +78,12 @@ #define DW_IC_TX_ABRT_SOURCE 0x80 #define DW_IC_ENABLE_STATUS 0x9c #define DW_IC_CLR_RESTART_DET 0xa8 +#define DW_IC_SCL_STUCK_AT_LOW_TIMEOUT 0xac +#define DW_IC_SDA_STUCK_AT_LOW_TIMEOUT 0xb0 #define DW_IC_COMP_PARAM_1 0xf4 #define DW_IC_COMP_VERSION 0xf8 #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ +#define DW_IC_BUS_CLEAR_MIN_VERS 0x3230302A /* "200*" == v2.00* */ #define DW_IC_COMP_TYPE 0xfc #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ @@ -110,6 +113,7 @@ #define DW_IC_ENABLE_ENABLE BIT(0) #define DW_IC_ENABLE_ABORT BIT(1) +#define DW_IC_ENABLE_BUS_RECOVERY BIT(3) #define DW_IC_STATUS_ACTIVITY BIT(0) #define DW_IC_STATUS_TFE BIT(2) @@ -117,13 +121,16 @@ #define DW_IC_STATUS_MASTER_ACTIVITY BIT(5) #define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) #define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) +#define DW_IC_STATUS_SDA_STUCK_NOT_RECOVERED BIT(11) #define DW_IC_SDA_HOLD_RX_SHIFT 16 #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) #define DW_IC_ERR_TX_ABRT 0x1 +#define DW_IC_TAR_SPECIAL BIT(11) #define DW_IC_TAR_10BITADDR_MASTER BIT(12) +#define DW_IC_TAR_SMBUS_QUICK_CMD BIT(16) #define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) #define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) @@ -162,6 +169,7 @@ #define ABRT_SLAVE_FLUSH_TXFIFO 13 #define ABRT_SLAVE_ARBLOST 14 #define ABRT_SLAVE_RD_INTX 15 +#define ABRT_SLAVE_SDA_STUCK_AT_LOW 17 #define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(ABRT_7B_ADDR_NOACK) #define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(ABRT_10ADDR1_NOACK) @@ -177,6 +185,7 @@ #define DW_IC_RX_ABRT_SLAVE_RD_INTX BIT(ABRT_SLAVE_RD_INTX) #define DW_IC_RX_ABRT_SLAVE_ARBLOST BIT(ABRT_SLAVE_ARBLOST) #define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO BIT(ABRT_SLAVE_FLUSH_TXFIFO) +#define DW_IC_TX_ABRT_SLAVE_SDA_STUCK_AT_LOW BIT(ABRT_SLAVE_SDA_STUCK_AT_LOW) #define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ DW_IC_TX_ABRT_10ADDR1_NOACK | \ @@ -295,6 +304,7 @@ struct dw_i2c_dev { u16 fp_lcnt; u16 hs_hcnt; u16 hs_lcnt; + u32 wanted_bus_speed; int (*acquire_lock)(void); void (*release_lock)(void); int semaphore_idx; diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index cbd88ffa5610..d4c440d12726 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -41,6 +41,34 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_CON, dev->master_cfg); } +static u32 linear_interpolate(u32 x, u32 x1, u32 x2, u32 y1, u32 y2) +{ + return ((x - x1) * y2 + (x2 - x) * y1) / (x2 - x1); +} + +static u16 u16_clamp(u32 v) +{ + return (u16)min(v, 0xffff); +} + +static void clock_calc(struct dw_i2c_dev *dev, u32 *hcnt, u32 *lcnt) +{ + struct i2c_timings *t = &dev->timings; + u32 wanted_khz = (dev->wanted_bus_speed ?: t->bus_freq_hz)/1000; + u32 clk_khz = i2c_dw_clk_rate(dev); + u32 min_high_ns = (wanted_khz <= 100) ? 4000 : + (wanted_khz <= 400) ? + linear_interpolate(wanted_khz, 100, 400, 4000, 600) : + linear_interpolate(wanted_khz, 400, 1000, 600, 260); + u32 high_cycles = (u32)(((u64)clk_khz * min_high_ns + 999999) / 1000000) + 1; + u32 extra_high_cycles = (u32)((u64)clk_khz * t->scl_fall_ns / 1000000); + u32 extra_low_cycles = (u32)((u64)clk_khz * t->scl_rise_ns / 1000000); + u32 period = ((u64)clk_khz + wanted_khz - 1) / wanted_khz; + + *hcnt = u16_clamp(high_cycles - extra_high_cycles); + *lcnt = u16_clamp(period - high_cycles - extra_low_cycles); +} + static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) { unsigned int comp_param1; @@ -48,6 +76,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) struct i2c_timings *t = &dev->timings; const char *fp_str = ""; u32 ic_clk; + u32 hcnt, lcnt; int ret; ret = i2c_dw_acquire_lock(dev); @@ -63,6 +92,8 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + clock_calc(dev, &hcnt, &lcnt); + /* Calculate SCL timing parameters for standard mode if not set */ if (!dev->ss_hcnt || !dev->ss_lcnt) { ic_clk = i2c_dw_clk_rate(dev); @@ -81,6 +112,8 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) scl_falling_time, 0); /* No offset */ } + dev->ss_hcnt = hcnt; + dev->ss_lcnt = lcnt; dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, dev->ss_lcnt); @@ -137,6 +170,8 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) scl_falling_time, 0); /* No offset */ } + dev->fs_hcnt = hcnt; + dev->fs_lcnt = lcnt; dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, dev->fs_hcnt, dev->fs_lcnt); @@ -187,10 +222,15 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) scl_falling_time, 0); /* No offset */ } + dev->hs_hcnt = hcnt; + dev->hs_lcnt = lcnt; dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", dev->hs_hcnt, dev->hs_lcnt); } + if (!dev->sda_hold_time) + dev->sda_hold_time = lcnt / 2; + ret = i2c_dw_set_sda_hold(dev); if (ret) return ret; @@ -211,6 +251,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) */ static int i2c_dw_init_master(struct dw_i2c_dev *dev) { + unsigned int timeout = 0; int ret; ret = i2c_dw_acquire_lock(dev); @@ -234,6 +275,17 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); } + if (dev->master_cfg & DW_IC_CON_BUS_CLEAR_CTRL) { + /* Set a sensible timeout if not already configured */ + regmap_read(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, &timeout); + if (timeout == ~0) { + /* Use 10ms as a timeout, which is 1000 cycles at 100kHz */ + timeout = i2c_dw_clk_rate(dev) * 10; /* clock rate is in kHz */ + regmap_write(dev->map, DW_IC_SDA_STUCK_AT_LOW_TIMEOUT, timeout); + regmap_write(dev->map, DW_IC_SCL_STUCK_AT_LOW_TIMEOUT, timeout); + } + } + /* Write SDA hold time if supported */ if (dev->sda_hold_time) regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); @@ -265,6 +317,10 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) ic_tar = DW_IC_TAR_10BITADDR_MASTER; } + /* Convert a zero-length read into an SMBUS quick command */ + if (!msgs[dev->msg_write_idx].len) + ic_tar = DW_IC_TAR_SPECIAL | DW_IC_TAR_SMBUS_QUICK_CMD; + regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER, ic_con); @@ -475,6 +531,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) regmap_read(dev->map, DW_IC_RXFLR, &flr); rx_limit = dev->rx_fifo_depth - flr; + /* Handle SMBUS quick commands */ + if (!buf_len) { + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) + regmap_write(dev->map, DW_IC_DATA_CMD, 0x300); + else + regmap_write(dev->map, DW_IC_DATA_CMD, 0x200); + } + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { u32 cmd = 0; @@ -913,14 +977,15 @@ static const struct i2c_algorithm i2c_dw_algo = { }; static const struct i2c_adapter_quirks i2c_dw_quirks = { - .flags = I2C_AQ_NO_ZERO_LEN, + .flags = 0, }; void i2c_dw_configure_master(struct dw_i2c_dev *dev) { struct i2c_timings *t = &dev->timings; - dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; + dev->functionality = I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_QUICK | + DW_IC_DEFAULT_FUNCTIONALITY; dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN; @@ -1002,6 +1067,7 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) struct i2c_adapter *adap = &dev->adapter; unsigned long irq_flags; unsigned int ic_con; + unsigned int id_ver; int ret; init_completion(&dev->cmd_complete); @@ -1036,7 +1102,11 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) if (ret) return ret; - if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL) + ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &id_ver); + if (ret) + return ret; + + if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL || id_ver >= DW_IC_BUS_CLEAR_MIN_VERS) dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL; ret = dev->init(dev); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index f4355b17bfbf..d83b19a37fd6 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -428,7 +428,9 @@ static int i2c_gpio_probe(struct platform_device *pdev) adap->dev.parent = dev; device_set_node(&adap->dev, fwnode); - adap->nr = pdev->id; + if (pdev->id != PLATFORM_DEVID_NONE || !pdev->dev.of_node || + of_property_read_u32(pdev->dev.of_node, "reg", &adap->nr)) + adap->nr = pdev->id; ret = i2c_bit_add_numbered_bus(adap); if (ret) return ret; diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 4d8690981a55..32274dd227ea 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -333,8 +333,13 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, if (muxc->dev->of_node) { struct device_node *dev_node = muxc->dev->of_node; struct device_node *mux_node, *child = NULL; + u32 base_nr = 0; u32 reg; + of_property_read_u32(dev_node, "base-nr", &base_nr); + if (!force_nr && base_nr) + force_nr = base_nr + chan_id; + if (muxc->arbitrator) mux_node = of_get_child_by_name(dev_node, "i2c-arb"); else if (muxc->gate) |
