diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Kconfig | 10 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 20 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 2 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-at91.c | 127 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-davinci.c | 24 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-exynos5.c | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 57 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-img-scb.c | 1412 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 354 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-meson.c | 492 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mpc.c | 18 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mxs.c | 2 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 133 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pxa.c | 19 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-rcar.c | 124 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-rk3x.c | 251 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 29 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 244 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-xiic.c | 58 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.c | 64 | ||||
-rw-r--r-- | drivers/i2c/i2c-mux.c | 12 | ||||
-rw-r--r-- | drivers/i2c/i2c-slave-eeprom.c | 170 |
23 files changed, 3456 insertions, 171 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b51a402752c4..8c9e619f3026 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -110,6 +110,16 @@ config I2C_STUB If you don't know what to do here, definitely say N. +config I2C_SLAVE + bool "I2C slave support" + +if I2C_SLAVE + +config I2C_SLAVE_EEPROM + tristate "I2C eeprom slave driver" + +endif + config I2C_DEBUG_CORE bool "I2C Core debugging messages" help diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 1722f50f2473..45095b3d16a9 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-y += algos/ busses/ muxes/ obj-$(CONFIG_I2C_STUB) += i2c-stub.o +obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG CFLAGS_i2c-core.o := -Wno-deprecated-declarations diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index b4d135cc2f39..c1351d9fb35b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -123,6 +123,7 @@ config I2C_I801 Wildcat Point-LP (PCH) BayTrail (SOC) Sunrise Point-H (PCH) + Sunrise Point-LP (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -523,6 +524,16 @@ config I2C_IBM_IIC This driver can also be built as a module. If so, the module will be called i2c-ibm_iic. +config I2C_IMG + tristate "Imagination Technologies I2C SCB Controller" + depends on MIPS || METAG || COMPILE_TEST + help + Say Y here if you want to use the IMG I2C SCB controller, + available on the TZ1090 and other IMG SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-img-scb. + config I2C_IMX tristate "IMX I2C interface" depends on ARCH_MXC @@ -553,6 +564,13 @@ config I2C_KEMPLD This driver can also be built as a module. If so, the module will be called i2c-kempld. +config I2C_MESON + tristate "Amlogic Meson I2C controller" + depends on ARCH_MESON + help + If you say yes to this option, support will be included for the + I2C interface on the Amlogic Meson family of SoCs. + config I2C_MPC tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC @@ -702,7 +720,7 @@ config I2C_RIIC config I2C_RK3X tristate "Rockchip RK3xxx I2C adapter" - depends on OF + depends on OF && COMMON_CLK help Say Y here to include support for the I2C adapter in Rockchip RK3xxx SoCs. diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index cdac7f15eab5..5e6c8223719e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -50,9 +50,11 @@ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o +obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o +obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index e05a672db3e5..87e2f142ae6c 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -31,10 +31,13 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/platform_data/dma-atmel.h> +#include <linux/pm_runtime.h> +#include <linux/pinctrl/consumer.h> #define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */ #define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */ #define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */ +#define AUTOSUSPEND_TIMEOUT 2000 /* AT91 TWI register definitions */ #define AT91_TWI_CR 0x0000 /* Control Register */ @@ -72,7 +75,6 @@ struct at91_twi_pdata { unsigned clk_max_div; unsigned clk_offset; bool has_unre_flag; - bool has_dma_support; struct at_dma_slave dma_slave; }; @@ -481,6 +483,10 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + goto out; + /* * The hardware can handle at most two messages concatenated by a * repeated start via it's internal address feature. @@ -488,18 +494,21 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) if (num > 2) { dev_err(dev->dev, "cannot handle more than two concatenated messages.\n"); - return 0; + ret = 0; + goto out; } else if (num == 2) { int internal_address = 0; int i; if (msg->flags & I2C_M_RD) { dev_err(dev->dev, "first transfer must be write.\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } if (msg->len > 3) { dev_err(dev->dev, "first message size must be <= 3.\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } /* 1st msg is put into the internal address, start with 2nd */ @@ -523,7 +532,12 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) ret = at91_do_twi_transfer(dev); - return (ret < 0) ? ret : num; + ret = (ret < 0) ? ret : num; +out: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; } static u32 at91_twi_func(struct i2c_adapter *adapter) @@ -541,35 +555,30 @@ static struct at91_twi_pdata at91rm9200_config = { .clk_max_div = 5, .clk_offset = 3, .has_unre_flag = true, - .has_dma_support = false, }; static struct at91_twi_pdata at91sam9261_config = { .clk_max_div = 5, .clk_offset = 4, .has_unre_flag = false, - .has_dma_support = false, }; static struct at91_twi_pdata at91sam9260_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, - .has_dma_support = false, }; static struct at91_twi_pdata at91sam9g20_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, - .has_dma_support = false, }; static struct at91_twi_pdata at91sam9g10_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, - .has_dma_support = false, }; static const struct platform_device_id at91_twi_devtypes[] = { @@ -598,7 +607,6 @@ static struct at91_twi_pdata at91sam9x5_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, - .has_dma_support = true, }; static const struct of_device_id atmel_twi_dt_ids[] = { @@ -627,30 +635,11 @@ static const struct of_device_id atmel_twi_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids); #endif -static bool filter(struct dma_chan *chan, void *pdata) -{ - struct at91_twi_pdata *sl_pdata = pdata; - struct at_dma_slave *sl; - - if (!sl_pdata) - return false; - - sl = &sl_pdata->dma_slave; - if (sl && (sl->dma_dev == chan->device->dev)) { - chan->private = sl; - return true; - } else { - return false; - } -} - static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) { int ret = 0; - struct at91_twi_pdata *pdata = dev->pdata; struct dma_slave_config slave_config; struct at91_twi_dma *dma = &dev->dma; - dma_cap_mask_t mask; memset(&slave_config, 0, sizeof(slave_config)); slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR; @@ -661,22 +650,17 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) slave_config.dst_maxburst = 1; slave_config.device_fc = false; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - dma->chan_tx = dma_request_slave_channel_compat(mask, filter, pdata, - dev->dev, "tx"); - if (!dma->chan_tx) { - dev_err(dev->dev, "can't get a DMA channel for tx\n"); - ret = -EBUSY; + dma->chan_tx = dma_request_slave_channel_reason(dev->dev, "tx"); + if (IS_ERR(dma->chan_tx)) { + ret = PTR_ERR(dma->chan_tx); + dma->chan_tx = NULL; goto error; } - dma->chan_rx = dma_request_slave_channel_compat(mask, filter, pdata, - dev->dev, "rx"); - if (!dma->chan_rx) { - dev_err(dev->dev, "can't get a DMA channel for rx\n"); - ret = -EBUSY; + dma->chan_rx = dma_request_slave_channel_reason(dev->dev, "rx"); + if (IS_ERR(dma->chan_rx)) { + ret = PTR_ERR(dma->chan_rx); + dma->chan_rx = NULL; goto error; } @@ -697,6 +681,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) sg_init_table(&dma->sg, 1); dma->buf_mapped = false; dma->xfer_in_progress = false; + dev->use_dma = true; dev_info(dev->dev, "using %s (tx) and %s (rx) for DMA transfers\n", dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); @@ -704,7 +689,8 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) return ret; error: - dev_info(dev->dev, "can't use DMA\n"); + if (ret != -EPROBE_DEFER) + dev_info(dev->dev, "can't use DMA, error %d\n", ret); if (dma->chan_rx) dma_release_channel(dma->chan_rx); if (dma->chan_tx) @@ -772,9 +758,10 @@ static int at91_twi_probe(struct platform_device *pdev) } clk_prepare_enable(dev->clk); - if (dev->pdata->has_dma_support) { - if (at91_twi_configure_dma(dev, phy_addr) == 0) - dev->use_dma = true; + if (dev->dev->of_node) { + rc = at91_twi_configure_dma(dev, phy_addr); + if (rc == -EPROBE_DEFER) + return rc; } rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", @@ -795,11 +782,20 @@ static int at91_twi_probe(struct platform_device *pdev) dev->adapter.timeout = AT91_I2C_TIMEOUT; dev->adapter.dev.of_node = pdev->dev.of_node; + pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(dev->dev); + pm_runtime_set_active(dev->dev); + pm_runtime_enable(dev->dev); + rc = i2c_add_numbered_adapter(&dev->adapter); if (rc) { dev_err(dev->dev, "Adapter %s registration failed\n", dev->adapter.name); clk_disable_unprepare(dev->clk); + + pm_runtime_disable(dev->dev); + pm_runtime_set_suspended(dev->dev); + return rc; } @@ -814,6 +810,9 @@ static int at91_twi_remove(struct platform_device *pdev) i2c_del_adapter(&dev->adapter); clk_disable_unprepare(dev->clk); + pm_runtime_disable(dev->dev); + pm_runtime_set_suspended(dev->dev); + return 0; } @@ -823,7 +822,9 @@ static int at91_twi_runtime_suspend(struct device *dev) { struct at91_twi_dev *twi_dev = dev_get_drvdata(dev); - clk_disable(twi_dev->clk); + clk_disable_unprepare(twi_dev->clk); + + pinctrl_pm_select_sleep_state(dev); return 0; } @@ -832,10 +833,38 @@ static int at91_twi_runtime_resume(struct device *dev) { struct at91_twi_dev *twi_dev = dev_get_drvdata(dev); - return clk_enable(twi_dev->clk); + pinctrl_pm_select_default_state(dev); + + return clk_prepare_enable(twi_dev->clk); +} + +static int at91_twi_suspend_noirq(struct device *dev) +{ + if (!pm_runtime_status_suspended(dev)) + at91_twi_runtime_suspend(dev); + + return 0; +} + +static int at91_twi_resume_noirq(struct device *dev) +{ + int ret; + + if (!pm_runtime_status_suspended(dev)) { + ret = at91_twi_runtime_resume(dev); + if (ret) + return ret; + } + + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + + return 0; } static const struct dev_pm_ops at91_twi_pm = { + .suspend_noirq = at91_twi_suspend_noirq, + .resume_noirq = at91_twi_resume_noirq, .runtime_suspend = at91_twi_runtime_suspend, .runtime_resume = at91_twi_runtime_resume, }; diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 01f0cd87a4a5..0aa1054711af 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -368,8 +368,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) flag |= DAVINCI_I2C_MDR_STP; davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, - dev->adapter.timeout); + r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); if (r == 0) { dev_err(dev->dev, "controller timed out\n"); davinci_i2c_recover_bus(dev); @@ -380,7 +379,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) if (dev->buf_len) { /* This should be 0 if all bytes were transferred * or dev->cmd_err denotes an error. - * A signal may have aborted the transfer. */ if (r >= 0) { dev_err(dev->dev, "abnormal termination buf_len=%i\n", @@ -634,13 +632,17 @@ static int davinci_i2c_probe(struct platform_device *pdev) { struct davinci_i2c_dev *dev; struct i2c_adapter *adap; - struct resource *mem, *irq; - int r; - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "no irq resource?\n"); - return -ENODEV; + struct resource *mem; + int r, irq; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + if (!irq) + irq = -ENXIO; + if (irq != -EPROBE_DEFER) + dev_err(&pdev->dev, + "can't get irq resource ret=%d\n", irq); + return irq; } dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev), @@ -655,7 +657,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) init_completion(&dev->xfr_complete); #endif dev->dev = &pdev->dev; - dev->irq = irq->start; + dev->irq = irq; dev->pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, dev); diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 81e6263cd7da..271533d564ec 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -457,7 +457,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) goto stop; } else if (int_status & HSI2C_INT_TIMEOUT) { dev_dbg(i2c->dev, "Accessing device timed out\n"); - i2c->state = -EAGAIN; + i2c->state = -ETIMEDOUT; goto stop; } } else if (int_status & HSI2C_INT_I2C) { @@ -476,7 +476,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) goto stop; } else if (trans_status & HSI2C_TIMEOUT_AUTO) { dev_dbg(i2c->dev, "Accessing device timed out\n"); - i2c->state = -EAGAIN; + i2c->state = -ETIMEDOUT; goto stop; } else if (trans_status & HSI2C_TRANS_DONE) { i2c->trans_done = 1; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 6ab4f1cb21f3..8fafb254e42a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -2,7 +2,7 @@ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com> - Copyright (C) 2007 - 2012 Jean Delvare <jdelvare@suse.de> + Copyright (C) 2007 - 2014 Jean Delvare <jdelvare@suse.de> Copyright (C) 2010 Intel Corporation, David Woodhouse <dwmw2@infradead.org> @@ -59,6 +59,7 @@ * Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes * BayTrail (SOC) 0x0f12 32 hard yes yes yes * Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes + * Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -109,12 +110,16 @@ /* PCI Address Constants */ #define SMBBAR 4 +#define SMBPCICTL 0x004 #define SMBPCISTS 0x006 #define SMBHSTCFG 0x040 /* Host status bits for SMBPCISTS */ #define SMBPCISTS_INTS 0x08 +/* Control bits for SMBPCICTL */ +#define SMBPCICTL_INTDIS 0x0400 + /* Host configuration bits for SMBHSTCFG */ #define SMBHSTCFG_HST_EN 1 #define SMBHSTCFG_SMB_SMI_EN 2 @@ -182,6 +187,7 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123 +#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23 struct i801_mux_config { char *gpio_chip; @@ -371,6 +377,7 @@ static int i801_transaction(struct i801_priv *priv, int xact) { int status; int result; + const struct i2c_adapter *adap = &priv->adapter; result = i801_check_pre(priv); if (result < 0) @@ -379,7 +386,14 @@ static int i801_transaction(struct i801_priv *priv, int xact) if (priv->features & FEATURE_IRQ) { outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, SMBHSTCNT(priv)); - wait_event(priv->waitq, (status = priv->status)); + result = wait_event_timeout(priv->waitq, + (status = priv->status), + adap->timeout); + if (!result) { + status = -ETIMEDOUT; + dev_warn(&priv->pci_dev->dev, + "Timeout waiting for interrupt!\n"); + } priv->status = 0; return i801_check_post(priv, status); } @@ -493,9 +507,6 @@ static irqreturn_t i801_isr(int irq, void *dev_id) return IRQ_NONE; status = inb_p(SMBHSTSTS(priv)); - if (status != 0x42) - dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status); - if (status & SMBHSTSTS_BYTE_DONE) i801_isr_byte_done(priv); @@ -527,6 +538,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, int smbcmd; int status; int result; + const struct i2c_adapter *adap = &priv->adapter; result = i801_check_pre(priv); if (result < 0) @@ -555,7 +567,14 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, priv->data = &data->block[1]; outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); - wait_event(priv->waitq, (status = priv->status)); + result = wait_event_timeout(priv->waitq, + (status = priv->status), + adap->timeout); + if (!result) { + status = -ETIMEDOUT; + dev_warn(&priv->pci_dev->dev, + "Timeout waiting for interrupt!\n"); + } priv->status = 0; return i801_check_post(priv, status); } @@ -829,6 +848,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) }, { 0, } }; @@ -1212,6 +1232,25 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + /* Default timeout in interrupt mode: 200 ms */ + priv->adapter.timeout = HZ / 5; + + if (priv->features & FEATURE_IRQ) { + u16 pcictl, pcists; + + /* Complain if an interrupt is already pending */ + pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists); + if (pcists & SMBPCISTS_INTS) + dev_warn(&dev->dev, "An interrupt is pending!\n"); + + /* Check if interrupts have been disabled */ + pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl); + if (pcictl & SMBPCICTL_INTDIS) { + dev_info(&dev->dev, "Interrupts are disabled\n"); + priv->features &= ~FEATURE_IRQ; + } + } + if (priv->features & FEATURE_IRQ) { init_waitqueue_head(&priv->waitq); @@ -1220,10 +1259,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) if (err) { dev_err(&dev->dev, "Failed to allocate irq %d: %d\n", dev->irq, err); - goto exit_release; + priv->features &= ~FEATURE_IRQ; } - dev_info(&dev->dev, "SMBus using PCI Interrupt\n"); } + dev_info(&dev->dev, "SMBus using %s\n", + priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling"); /* set up the sysfs linkage to our parent device */ priv->adapter.dev.parent = &dev->dev; @@ -1250,7 +1290,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) exit_free_irq: if (priv->features & FEATURE_IRQ) free_irq(dev->irq, priv); -exit_release: pci_release_region(dev, SMBBAR); exit: kfree(priv); diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c new file mode 100644 index 000000000000..0fcc1694c607 --- /dev/null +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -0,0 +1,1412 @@ +/* + * I2C adapter for the IMG Serial Control Bus (SCB) IP block. + * + * Copyright (C) 2009, 2010, 2012, 2014 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * There are three ways that this I2C controller can be driven: + * + * - Raw control of the SDA and SCK signals. + * + * This corresponds to MODE_RAW, which takes control of the signals + * directly for a certain number of clock cycles (the INT_TIMING + * interrupt can be used for timing). + * + * - Atomic commands. A low level I2C symbol (such as generate + * start/stop/ack/nack bit, generate byte, receive byte, and receive + * ACK) is given to the hardware, with detection of completion by bits + * in the LINESTAT register. + * + * This mode of operation is used by MODE_ATOMIC, which uses an I2C + * state machine in the interrupt handler to compose/react to I2C + * transactions using atomic mode commands, and also by MODE_SEQUENCE, + * which emits a simple fixed sequence of atomic mode commands. + * + * Due to software control, the use of atomic commands usually results + * in suboptimal use of the bus, with gaps between the I2C symbols while + * the driver decides what to do next. + * + * - Automatic mode. A bus address, and whether to read/write is + * specified, and the hardware takes care of the I2C state machine, + * using a FIFO to send/receive bytes of data to an I2C slave. The + * driver just has to keep the FIFO drained or filled in response to the + * appropriate FIFO interrupts. + * + * This corresponds to MODE_AUTOMATIC, which manages the FIFOs and deals + * with control of repeated start bits between I2C messages. + * + * Use of automatic mode and the FIFO can make much more efficient use + * of the bus compared to individual atomic commands, with potentially + * no wasted time between I2C symbols or I2C messages. + * + * In most cases MODE_AUTOMATIC is used, however if any of the messages in + * a transaction are zero byte writes (e.g. used by i2cdetect for probing + * the bus), MODE_ATOMIC must be used since automatic mode is normally + * started by the writing of data into the FIFO. + * + * The other modes are used in specific circumstances where MODE_ATOMIC and + * MODE_AUTOMATIC aren't appropriate. MODE_RAW is used to implement a bus + * recovery routine. MODE_SEQUENCE is used to reset the bus and make sure + * it is in a sane state. + * + * Notice that the driver implements a timer-based timeout mechanism. + * The reason for this mechanism is to reduce the number of interrupts + * received in automatic mode. + * + * The driver would get a slave event and transaction done interrupts for + * each atomic mode command that gets completed. However, these events are + * not needed in automatic mode, becase those atomic mode commands are + * managed automatically by the hardware. + * + * In practice, normal I2C transactions will be complete well before you + * get the timer interrupt, as the timer is re-scheduled during FIFO + * maintenance and disabled after the transaction is complete. + * + * In this way normal automatic mode operation isn't impacted by + * unnecessary interrupts, but the exceptional abort condition can still be + * detected (with a slight delay). + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/timer.h> + +/* Register offsets */ + +#define SCB_STATUS_REG 0x00 +#define SCB_OVERRIDE_REG 0x04 +#define SCB_READ_ADDR_REG 0x08 +#define SCB_READ_COUNT_REG 0x0c +#define SCB_WRITE_ADDR_REG 0x10 +#define SCB_READ_DATA_REG 0x14 +#define SCB_WRITE_DATA_REG 0x18 +#define SCB_FIFO_STATUS_REG 0x1c +#define SCB_CONTROL_SOFT_RESET 0x1f +#define SCB_CLK_SET_REG 0x3c +#define SCB_INT_STATUS_REG 0x40 +#define SCB_INT_CLEAR_REG 0x44 +#define SCB_INT_MASK_REG 0x48 +#define SCB_CONTROL_REG 0x4c +#define SCB_TIME_TPL_REG 0x50 +#define SCB_TIME_TPH_REG 0x54 +#define SCB_TIME_TP2S_REG 0x58 +#define SCB_TIME_TBI_REG 0x60 +#define SCB_TIME_TSL_REG 0x64 +#define SCB_TIME_TDL_REG 0x68 +#define SCB_TIME_TSDL_REG 0x6c +#define SCB_TIME_TSDH_REG 0x70 +#define SCB_READ_XADDR_REG 0x74 +#define SCB_WRITE_XADDR_REG 0x78 +#define SCB_WRITE_COUNT_REG 0x7c +#define SCB_CORE_REV_REG 0x80 +#define SCB_TIME_TCKH_REG 0x84 +#define SCB_TIME_TCKL_REG 0x88 +#define SCB_FIFO_FLUSH_REG 0x8c +#define SCB_READ_FIFO_REG 0x94 +#define SCB_CLEAR_REG 0x98 + +/* SCB_CONTROL_REG bits */ + +#define SCB_CONTROL_CLK_ENABLE 0x1e0 +#define SCB_CONTROL_TRANSACTION_HALT 0x200 + +#define FIFO_READ_FULL BIT(0) +#define FIFO_READ_EMPTY BIT(1) +#define FIFO_WRITE_FULL BIT(2) +#define FIFO_WRITE_EMPTY BIT(3) + +/* SCB_CLK_SET_REG bits */ +#define SCB_FILT_DISABLE BIT(31) +#define SCB_FILT_BYPASS BIT(30) +#define SCB_FILT_INC_MASK 0x7f +#define SCB_FILT_INC_SHIFT 16 +#define SCB_INC_MASK 0x7f +#define SCB_INC_SHIFT 8 + +/* SCB_INT_*_REG bits */ + +#define INT_BUS_INACTIVE BIT(0) +#define INT_UNEXPECTED_START BIT(1) +#define INT_SCLK_LOW_TIMEOUT BIT(2) +#define INT_SDAT_LOW_TIMEOUT BIT(3) +#define INT_WRITE_ACK_ERR BIT(4) +#define INT_ADDR_ACK_ERR BIT(5) +#define INT_FIFO_FULL BIT(9) +#define INT_FIFO_FILLING BIT(10) +#define INT_FIFO_EMPTY BIT(11) +#define INT_FIFO_EMPTYING BIT(12) +#define INT_TRANSACTION_DONE BIT(15) +#define INT_SLAVE_EVENT BIT(16) +#define INT_TIMING BIT(18) + +#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING) +#define INT_FIFO_EMPTY_EMPTYING (INT_FIFO_EMPTY | INT_FIFO_EMPTYING) + +/* Level interrupts need clearing after handling instead of before */ +#define INT_LEVEL 0x01e00 + +/* Don't allow any interrupts while the clock may be off */ +#define INT_ENABLE_MASK_INACTIVE 0x00000 + +/* Interrupt masks for the different driver modes */ + +#define INT_ENABLE_MASK_RAW INT_TIMING + +#define INT_ENABLE_MASK_ATOMIC (INT_TRANSACTION_DONE | \ + INT_SLAVE_EVENT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR) + +#define INT_ENABLE_MASK_AUTOMATIC (INT_SCLK_LOW_TIMEOUT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR | \ + INT_FIFO_FULL | \ + INT_FIFO_FILLING | \ + INT_FIFO_EMPTY | \ + INT_FIFO_EMPTYING) + +#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \ + INT_ADDR_ACK_ERR | \ + INT_WRITE_ACK_ERR) + +/* SCB_STATUS_REG fields */ + +#define LINESTAT_SCLK_LINE_STATUS BIT(0) +#define LINESTAT_SCLK_EN BIT(1) +#define LINESTAT_SDAT_LINE_STATUS BIT(2) +#define LINESTAT_SDAT_EN BIT(3) +#define LINESTAT_DET_START_STATUS BIT(4) +#define LINESTAT_DET_STOP_STATUS BIT(5) +#define LINESTAT_DET_ACK_STATUS BIT(6) +#define LINESTAT_DET_NACK_STATUS BIT(7) +#define LINESTAT_BUS_IDLE BIT(8) +#define LINESTAT_T_DONE_STATUS BIT(9) +#define LINESTAT_SCLK_OUT_STATUS BIT(10) +#define LINESTAT_SDAT_OUT_STATUS BIT(11) +#define LINESTAT_GEN_LINE_MASK_STATUS BIT(12) +#define LINESTAT_START_BIT_DET BIT(13) +#define LINESTAT_STOP_BIT_DET BIT(14) +#define LINESTAT_ACK_DET BIT(15) +#define LINESTAT_NACK_DET BIT(16) +#define LINESTAT_INPUT_HELD_V BIT(17) +#define LINESTAT_ABORT_DET BIT(18) +#define LINESTAT_ACK_OR_NACK_DET (LINESTAT_ACK_DET | LINESTAT_NACK_DET) +#define LINESTAT_INPUT_DATA 0xff000000 +#define LINESTAT_INPUT_DATA_SHIFT 24 + +#define LINESTAT_CLEAR_SHIFT 13 +#define LINESTAT_LATCHED (0x3f << LINESTAT_CLEAR_SHIFT) + +/* SCB_OVERRIDE_REG fields */ + +#define OVERRIDE_SCLK_OVR BIT(0) +#define OVERRIDE_SCLKEN_OVR BIT(1) +#define OVERRIDE_SDAT_OVR BIT(2) +#define OVERRIDE_SDATEN_OVR BIT(3) +#define OVERRIDE_MASTER BIT(9) +#define OVERRIDE_LINE_OVR_EN BIT(10) +#define OVERRIDE_DIRECT BIT(11) +#define OVERRIDE_CMD_SHIFT 4 +#define OVERRIDE_CMD_MASK 0x1f +#define OVERRIDE_DATA_SHIFT 24 + +#define OVERRIDE_SCLK_DOWN (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SCLKEN_OVR) +#define OVERRIDE_SCLK_UP (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SCLKEN_OVR | \ + OVERRIDE_SCLK_OVR) +#define OVERRIDE_SDAT_DOWN (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SDATEN_OVR) +#define OVERRIDE_SDAT_UP (OVERRIDE_LINE_OVR_EN | \ + OVERRIDE_SDATEN_OVR | \ + OVERRIDE_SDAT_OVR) + +/* OVERRIDE_CMD values */ + +#define CMD_PAUSE 0x00 +#define CMD_GEN_DATA 0x01 +#define CMD_GEN_START 0x02 +#define CMD_GEN_STOP 0x03 +#define CMD_GEN_ACK 0x04 +#define CMD_GEN_NACK 0x05 +#define CMD_RET_DATA 0x08 +#define CMD_RET_ACK 0x09 + +/* Fixed timing values */ + +#define TIMEOUT_TBI 0x0 +#define TIMEOUT_TSL 0xffff +#define TIMEOUT_TDL 0x0 + +/* Transaction timeout */ + +#define IMG_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +/* + * Worst incs are 1 (innacurate) and 16*256 (irregular). + * So a sensible inc is the logarithmic mean: 64 (2^6), which is + * in the middle of the valid range (0-127). + */ +#define SCB_OPT_INC 64 + +/* Setup the clock enable filtering for 25 ns */ +#define SCB_FILT_GLITCH 25 + +/* + * Bits to return from interrupt handler functions for different modes. + * This delays completion until we've finished with the registers, so that the + * function waiting for completion can safely disable the clock to save power. + */ +#define ISR_COMPLETE_M BIT(31) +#define ISR_FATAL_M BIT(30) +#define ISR_WAITSTOP BIT(29) +#define ISR_STATUS_M 0x0000ffff /* contains +ve errno */ +#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err))) +#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M) + +#define REL_SOC_IP_SCB_2_2_1 0x00020201 + +enum img_i2c_mode { + MODE_INACTIVE, + MODE_RAW, + MODE_ATOMIC, + MODE_AUTOMATIC, + MODE_SEQUENCE, + MODE_FATAL, + MODE_WAITSTOP, + MODE_SUSPEND, +}; + +/* Timing parameters for i2c modes (in ns) */ +struct img_i2c_timings { + const char *name; + unsigned int max_bitrate; + unsigned int tckh, tckl, tsdh, tsdl; + unsigned int tp2s, tpl, tph; +}; + +/* The timings array must be ordered from slower to faster */ +static struct img_i2c_timings timings[] = { + /* Standard mode */ + { + .name = "standard", + .max_bitrate = 100000, + .tckh = 4000, + .tckl = 4700, + .tsdh = 4700, + .tsdl = 8700, + .tp2s = 4700, + .tpl = 4700, + .tph = 4000, + }, + /* Fast mode */ + { + .name = "fast", + .max_bitrate = 400000, + .tckh = 600, + .tckl = 1300, + .tsdh = 600, + .tsdl = 1200, + .tp2s = 1300, + .tpl = 600, + .tph = 600, + }, +}; + +/* Reset dance */ +static u8 img_i2c_reset_seq[] = { CMD_GEN_START, + CMD_GEN_DATA, 0xff, + CMD_RET_ACK, + CMD_GEN_START, + CMD_GEN_STOP, + 0 }; +/* Just issue a stop (after an abort condition) */ +static u8 img_i2c_stop_seq[] = { CMD_GEN_STOP, + 0 }; + +/* We're interested in different interrupts depending on the mode */ +static unsigned int img_i2c_int_enable_by_mode[] = { + [MODE_INACTIVE] = INT_ENABLE_MASK_INACTIVE, + [MODE_RAW] = INT_ENABLE_MASK_RAW, + [MODE_ATOMIC] = INT_ENABLE_MASK_ATOMIC, + [MODE_AUTOMATIC] = INT_ENABLE_MASK_AUTOMATIC, + [MODE_SEQUENCE] = INT_ENABLE_MASK_ATOMIC, + [MODE_FATAL] = 0, + [MODE_WAITSTOP] = INT_ENABLE_MASK_WAITSTOP, + [MODE_SUSPEND] = 0, +}; + +/* Atomic command names */ +static const char * const img_i2c_atomic_cmd_names[] = { + [CMD_PAUSE] = "PAUSE", + [CMD_GEN_DATA] = "GEN_DATA", + [CMD_GEN_START] = "GEN_START", + [CMD_GEN_STOP] = "GEN_STOP", + [CMD_GEN_ACK] = "GEN_ACK", + [CMD_GEN_NACK] = "GEN_NACK", + [CMD_RET_DATA] = "RET_DATA", + [CMD_RET_ACK] = "RET_ACK", +}; + +struct img_i2c { + struct i2c_adapter adap; + + void __iomem *base; + + /* + * The scb core clock is used to get the input frequency, and to disable + * it after every set of transactions to save some power. + */ + struct clk *scb_clk, *sys_clk; + unsigned int bitrate; + bool need_wr_rd_fence; + + /* state */ + struct completion msg_complete; + spinlock_t lock; /* lock before doing anything with the state */ + struct i2c_msg msg; + + /* After the last transaction, wait for a stop bit */ + bool last_msg; + int msg_status; + + enum img_i2c_mode mode; + u32 int_enable; /* depends on mode */ + u32 line_status; /* line status over command */ + + /* + * To avoid slave event interrupts in automatic mode, use a timer to + * poll the abort condition if we don't get an interrupt for too long. + */ + struct timer_list check_timer; + bool t_halt; + + /* atomic mode state */ + bool at_t_done; + bool at_slave_event; + int at_cur_cmd; + u8 at_cur_data; + + /* Sequence: either reset or stop. See img_i2c_sequence. */ + u8 *seq; + + /* raw mode */ + unsigned int raw_timeout; +}; + +static void img_i2c_writel(struct img_i2c *i2c, u32 offset, u32 value) +{ + writel(value, i2c->base + offset); +} + +static u32 img_i2c_readl(struct img_i2c *i2c, u32 offset) +{ + return readl(i2c->base + offset); +} + +/* + * The code to read from the master read fifo, and write to the master + * write fifo, checks a bit in an SCB register before every byte to + * ensure that the fifo is not full (write fifo) or empty (read fifo). + * Due to clock domain crossing inside the SCB block the updated value + * of this bit is only visible after 2 cycles. + * + * The scb_wr_rd_fence() function does 2 dummy writes (to the read-only + * revision register), and it's called after reading from or writing to the + * fifos to ensure that subsequent reads of the fifo status bits do not read + * stale values. + */ +static void img_i2c_wr_rd_fence(struct img_i2c *i2c) +{ + if (i2c->need_wr_rd_fence) { + img_i2c_writel(i2c, SCB_CORE_REV_REG, 0); + img_i2c_writel(i2c, SCB_CORE_REV_REG, 0); + } +} + +static void img_i2c_switch_mode(struct img_i2c *i2c, enum img_i2c_mode mode) +{ + i2c->mode = mode; + i2c->int_enable = img_i2c_int_enable_by_mode[mode]; + i2c->line_status = 0; +} + +static void img_i2c_raw_op(struct img_i2c *i2c) +{ + i2c->raw_timeout = 0; + img_i2c_writel(i2c, SCB_OVERRIDE_REG, + OVERRIDE_SCLKEN_OVR | + OVERRIDE_SDATEN_OVR | + OVERRIDE_MASTER | + OVERRIDE_LINE_OVR_EN | + OVERRIDE_DIRECT | + ((i2c->at_cur_cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) | + (i2c->at_cur_data << OVERRIDE_DATA_SHIFT)); +} + +static const char *img_i2c_atomic_op_name(unsigned int cmd) +{ + if (unlikely(cmd >= ARRAY_SIZE(img_i2c_atomic_cmd_names))) + return "UNKNOWN"; + return img_i2c_atomic_cmd_names[cmd]; +} + +/* Send a single atomic mode command to the hardware */ +static void img_i2c_atomic_op(struct img_i2c *i2c, int cmd, u8 data) +{ + i2c->at_cur_cmd = cmd; + i2c->at_cur_data = data; + + /* work around lack of data setup time when generating data */ + if (cmd == CMD_GEN_DATA && i2c->mode == MODE_ATOMIC) { + u32 line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + + if (line_status & LINESTAT_SDAT_LINE_STATUS && !(data & 0x80)) { + /* hold the data line down for a moment */ + img_i2c_switch_mode(i2c, MODE_RAW); + img_i2c_raw_op(i2c); + return; + } + } + + dev_dbg(i2c->adap.dev.parent, + "atomic cmd=%s (%d) data=%#x\n", + img_i2c_atomic_op_name(cmd), cmd, data); + i2c->at_t_done = (cmd == CMD_RET_DATA || cmd == CMD_RET_ACK); + i2c->at_slave_event = false; + i2c->line_status = 0; + + img_i2c_writel(i2c, SCB_OVERRIDE_REG, + ((cmd & OVERRIDE_CMD_MASK) << OVERRIDE_CMD_SHIFT) | + OVERRIDE_MASTER | + OVERRIDE_DIRECT | + (data << OVERRIDE_DATA_SHIFT)); +} + +/* Start a transaction in atomic mode */ +static void img_i2c_atomic_start(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_ATOMIC); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + img_i2c_atomic_op(i2c, CMD_GEN_START, 0x00); +} + +static void img_i2c_soft_reset(struct img_i2c *i2c) +{ + i2c->t_halt = false; + img_i2c_writel(i2c, SCB_CONTROL_REG, 0); + img_i2c_writel(i2c, SCB_CONTROL_REG, + SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET); +} + +/* enable or release transaction halt for control of repeated starts */ +static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt) +{ + u32 val; + + if (i2c->t_halt == t_halt) + return; + i2c->t_halt = t_halt; + val = img_i2c_readl(i2c, SCB_CONTROL_REG); + if (t_halt) + val |= SCB_CONTROL_TRANSACTION_HALT; + else + val &= ~SCB_CONTROL_TRANSACTION_HALT; + img_i2c_writel(i2c, SCB_CONTROL_REG, val); +} + +/* Drain data from the FIFO into the buffer (automatic mode) */ +static void img_i2c_read_fifo(struct img_i2c *i2c) +{ + while (i2c->msg.len) { + u32 fifo_status; + u8 data; + + fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); + if (fifo_status & FIFO_READ_EMPTY) + break; + + data = img_i2c_readl(i2c, SCB_READ_DATA_REG); + *i2c->msg.buf = data; + + img_i2c_writel(i2c, SCB_READ_FIFO_REG, 0xff); + img_i2c_wr_rd_fence(i2c); + i2c->msg.len--; + i2c->msg.buf++; + } +} + +/* Fill the FIFO with data from the buffer (automatic mode) */ +static void img_i2c_write_fifo(struct img_i2c *i2c) +{ + while (i2c->msg.len) { + u32 fifo_status; + + fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); + if (fifo_status & FIFO_WRITE_FULL) + break; + + img_i2c_writel(i2c, SCB_WRITE_DATA_REG, *i2c->msg.buf); + img_i2c_wr_rd_fence(i2c); + i2c->msg.len--; + i2c->msg.buf++; + } + + /* Disable fifo emptying interrupt if nothing more to write */ + if (!i2c->msg.len) + i2c->int_enable &= ~INT_FIFO_EMPTYING; +} + +/* Start a read transaction in automatic mode */ +static void img_i2c_read(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_AUTOMATIC); + if (!i2c->last_msg) + i2c->int_enable |= INT_SLAVE_EVENT; + + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr); + img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len); + + img_i2c_transaction_halt(i2c, false); + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); +} + +/* Start a write transaction in automatic mode */ +static void img_i2c_write(struct img_i2c *i2c) +{ + img_i2c_switch_mode(i2c, MODE_AUTOMATIC); + if (!i2c->last_msg) + i2c->int_enable |= INT_SLAVE_EVENT; + + img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr); + img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len); + + img_i2c_transaction_halt(i2c, false); + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); + img_i2c_write_fifo(i2c); + + /* img_i2c_write_fifo() may modify int_enable */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); +} + +/* + * Indicate that the transaction is complete. This is called from the + * ISR to wake up the waiting thread, after which the ISR must not + * access any more SCB registers. + */ +static void img_i2c_complete_transaction(struct img_i2c *i2c, int status) +{ + img_i2c_switch_mode(i2c, MODE_INACTIVE); + if (status) { + i2c->msg_status = status; + img_i2c_transaction_halt(i2c, false); + } + complete(&i2c->msg_complete); +} + +static unsigned int img_i2c_raw_atomic_delay_handler(struct img_i2c *i2c, + u32 int_status, u32 line_status) +{ + /* Stay in raw mode for this, so we don't just loop infinitely */ + img_i2c_atomic_op(i2c, i2c->at_cur_cmd, i2c->at_cur_data); + img_i2c_switch_mode(i2c, MODE_ATOMIC); + return 0; +} + +static unsigned int img_i2c_raw(struct img_i2c *i2c, u32 int_status, + u32 line_status) +{ + if (int_status & INT_TIMING) { + if (i2c->raw_timeout == 0) + return img_i2c_raw_atomic_delay_handler(i2c, + int_status, line_status); + --i2c->raw_timeout; + } + return 0; +} + +static unsigned int img_i2c_sequence(struct img_i2c *i2c, u32 int_status) +{ + static const unsigned int continue_bits[] = { + [CMD_GEN_START] = LINESTAT_START_BIT_DET, + [CMD_GEN_DATA] = LINESTAT_INPUT_HELD_V, + [CMD_RET_ACK] = LINESTAT_ACK_DET | LINESTAT_NACK_DET, + [CMD_RET_DATA] = LINESTAT_INPUT_HELD_V, + [CMD_GEN_STOP] = LINESTAT_STOP_BIT_DET, + }; + int next_cmd = -1; + u8 next_data = 0x00; + + if (int_status & INT_SLAVE_EVENT) + i2c->at_slave_event = true; + if (int_status & INT_TRANSACTION_DONE) + i2c->at_t_done = true; + + if (!i2c->at_slave_event || !i2c->at_t_done) + return 0; + + /* wait if no continue bits are set */ + if (i2c->at_cur_cmd >= 0 && + i2c->at_cur_cmd < ARRAY_SIZE(continue_bits)) { + unsigned int cont_bits = continue_bits[i2c->at_cur_cmd]; + + if (cont_bits) { + cont_bits |= LINESTAT_ABORT_DET; + if (!(i2c->line_status & cont_bits)) + return 0; + } + } + + /* follow the sequence of commands in i2c->seq */ + next_cmd = *i2c->seq; + /* stop on a nil */ + if (!next_cmd) { + img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0); + return ISR_COMPLETE(0); + } + /* when generating data, the next byte is the data */ + if (next_cmd == CMD_GEN_DATA) { + ++i2c->seq; + next_data = *i2c->seq; + } + ++i2c->seq; + img_i2c_atomic_op(i2c, next_cmd, next_data); + + return 0; +} + +static void img_i2c_reset_start(struct img_i2c *i2c) +{ + /* Initiate the magic dance */ + img_i2c_switch_mode(i2c, MODE_SEQUENCE); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + i2c->seq = img_i2c_reset_seq; + i2c->at_slave_event = true; + i2c->at_t_done = true; + i2c->at_cur_cmd = -1; + + /* img_i2c_reset_seq isn't empty so the following won't fail */ + img_i2c_sequence(i2c, 0); +} + +static void img_i2c_stop_start(struct img_i2c *i2c) +{ + /* Initiate a stop bit sequence */ + img_i2c_switch_mode(i2c, MODE_SEQUENCE); + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + i2c->seq = img_i2c_stop_seq; + i2c->at_slave_event = true; + i2c->at_t_done = true; + i2c->at_cur_cmd = -1; + + /* img_i2c_stop_seq isn't empty so the following won't fail */ + img_i2c_sequence(i2c, 0); +} + +static unsigned int img_i2c_atomic(struct img_i2c *i2c, + u32 int_status, + u32 line_status) +{ + int next_cmd = -1; + u8 next_data = 0x00; + + if (int_status & INT_SLAVE_EVENT) + i2c->at_slave_event = true; + if (int_status & INT_TRANSACTION_DONE) + i2c->at_t_done = true; + + if (!i2c->at_slave_event || !i2c->at_t_done) + goto next_atomic_cmd; + if (i2c->line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, "abort condition detected\n"); + next_cmd = CMD_GEN_STOP; + i2c->msg_status = -EIO; + goto next_atomic_cmd; + } + + /* i2c->at_cur_cmd may have completed */ + switch (i2c->at_cur_cmd) { + case CMD_GEN_START: + next_cmd = CMD_GEN_DATA; + next_data = (i2c->msg.addr << 1); + if (i2c->msg.flags & I2C_M_RD) + next_data |= 0x1; + break; + case CMD_GEN_DATA: + if (i2c->line_status & LINESTAT_INPUT_HELD_V) + next_cmd = CMD_RET_ACK; + break; + case CMD_RET_ACK: + if (i2c->line_status & LINESTAT_ACK_DET) { + if (i2c->msg.len == 0) { + next_cmd = CMD_GEN_STOP; + } else if (i2c->msg.flags & I2C_M_RD) { + next_cmd = CMD_RET_DATA; + } else { + next_cmd = CMD_GEN_DATA; + next_data = *i2c->msg.buf; + --i2c->msg.len; + ++i2c->msg.buf; + } + } else if (i2c->line_status & LINESTAT_NACK_DET) { + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + } + break; + case CMD_RET_DATA: + if (i2c->line_status & LINESTAT_INPUT_HELD_V) { + *i2c->msg.buf = (i2c->line_status & + LINESTAT_INPUT_DATA) + >> LINESTAT_INPUT_DATA_SHIFT; + --i2c->msg.len; + ++i2c->msg.buf; + if (i2c->msg.len) + next_cmd = CMD_GEN_ACK; + else + next_cmd = CMD_GEN_NACK; + } + break; + case CMD_GEN_ACK: + if (i2c->line_status & LINESTAT_ACK_DET) { + next_cmd = CMD_RET_DATA; + } else { + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + } + break; + case CMD_GEN_NACK: + next_cmd = CMD_GEN_STOP; + break; + case CMD_GEN_STOP: + img_i2c_writel(i2c, SCB_OVERRIDE_REG, 0); + return ISR_COMPLETE(0); + default: + dev_err(i2c->adap.dev.parent, "bad atomic command %d\n", + i2c->at_cur_cmd); + i2c->msg_status = -EIO; + next_cmd = CMD_GEN_STOP; + break; + } + +next_atomic_cmd: + if (next_cmd != -1) { + /* don't actually stop unless we're the last transaction */ + if (next_cmd == CMD_GEN_STOP && !i2c->msg_status && + !i2c->last_msg) + return ISR_COMPLETE(0); + img_i2c_atomic_op(i2c, next_cmd, next_data); + } + return 0; +} + +/* + * Timer function to check if something has gone wrong in automatic mode (so we + * don't have to handle so many interrupts just to catch an exception). + */ +static void img_i2c_check_timer(unsigned long arg) +{ + struct img_i2c *i2c = (struct img_i2c *)arg; + unsigned long flags; + unsigned int line_status; + + spin_lock_irqsave(&i2c->lock, flags); + line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + + /* check for an abort condition */ + if (line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, + "abort condition detected by check timer\n"); + /* enable slave event interrupt mask to trigger irq */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, + i2c->int_enable | INT_SLAVE_EVENT); + } + + spin_unlock_irqrestore(&i2c->lock, flags); +} + +static unsigned int img_i2c_auto(struct img_i2c *i2c, + unsigned int int_status, + unsigned int line_status) +{ + if (int_status & (INT_WRITE_ACK_ERR | INT_ADDR_ACK_ERR)) + return ISR_COMPLETE(EIO); + + if (line_status & LINESTAT_ABORT_DET) { + dev_dbg(i2c->adap.dev.parent, "abort condition detected\n"); + /* empty the read fifo */ + if ((i2c->msg.flags & I2C_M_RD) && + (int_status & INT_FIFO_FULL_FILLING)) + img_i2c_read_fifo(i2c); + /* use atomic mode and try to force a stop bit */ + i2c->msg_status = -EIO; + img_i2c_stop_start(i2c); + return 0; + } + + /* Enable transaction halt on start bit */ + if (!i2c->last_msg && i2c->line_status & LINESTAT_START_BIT_DET) { + img_i2c_transaction_halt(i2c, true); + /* we're no longer interested in the slave event */ + i2c->int_enable &= ~INT_SLAVE_EVENT; + } + + mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); + + if (i2c->msg.flags & I2C_M_RD) { + if (int_status & INT_FIFO_FULL_FILLING) { + img_i2c_read_fifo(i2c); + if (i2c->msg.len == 0) + return ISR_WAITSTOP; + } + } else { + if (int_status & INT_FIFO_EMPTY_EMPTYING) { + /* + * The write fifo empty indicates that we're in the + * last byte so it's safe to start a new write + * transaction without losing any bytes from the + * previous one. + * see 2.3.7 Repeated Start Transactions. + */ + if ((int_status & INT_FIFO_EMPTY) && + i2c->msg.len == 0) + return ISR_WAITSTOP; + img_i2c_write_fifo(i2c); + } + } + + return 0; +} + +static irqreturn_t img_i2c_isr(int irq, void *dev_id) +{ + struct img_i2c *i2c = (struct img_i2c *)dev_id; + u32 int_status, line_status; + /* We handle transaction completion AFTER accessing registers */ + unsigned int hret; + + /* Read interrupt status register. */ + int_status = img_i2c_readl(i2c, SCB_INT_STATUS_REG); + /* Clear detected interrupts. */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status); + + /* + * Read line status and clear it until it actually is clear. We have + * to be careful not to lose any line status bits that get latched. + */ + line_status = img_i2c_readl(i2c, SCB_STATUS_REG); + if (line_status & LINESTAT_LATCHED) { + img_i2c_writel(i2c, SCB_CLEAR_REG, + (line_status & LINESTAT_LATCHED) + >> LINESTAT_CLEAR_SHIFT); + img_i2c_wr_rd_fence(i2c); + } + + spin_lock(&i2c->lock); + + /* Keep track of line status bits received */ + i2c->line_status &= ~LINESTAT_INPUT_DATA; + i2c->line_status |= line_status; + + /* + * Certain interrupts indicate that sclk low timeout is not + * a problem. If any of these are set, just continue. + */ + if ((int_status & INT_SCLK_LOW_TIMEOUT) && + !(int_status & (INT_SLAVE_EVENT | + INT_FIFO_EMPTY | + INT_FIFO_FULL))) { + dev_crit(i2c->adap.dev.parent, + "fatal: clock low timeout occurred %s addr 0x%02x\n", + (i2c->msg.flags & I2C_M_RD) ? "reading" : "writing", + i2c->msg.addr); + hret = ISR_FATAL(EIO); + goto out; + } + + if (i2c->mode == MODE_ATOMIC) + hret = img_i2c_atomic(i2c, int_status, line_status); + else if (i2c->mode == MODE_AUTOMATIC) + hret = img_i2c_auto(i2c, int_status, line_status); + else if (i2c->mode == MODE_SEQUENCE) + hret = img_i2c_sequence(i2c, int_status); + else if (i2c->mode == MODE_WAITSTOP && (int_status & INT_SLAVE_EVENT) && + (line_status & LINESTAT_STOP_BIT_DET)) + hret = ISR_COMPLETE(0); + else if (i2c->mode == MODE_RAW) + hret = img_i2c_raw(i2c, int_status, line_status); + else + hret = 0; + + /* Clear detected level interrupts. */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, int_status & INT_LEVEL); + +out: + if (hret & ISR_WAITSTOP) { + /* + * Only wait for stop on last message. + * Also we may already have detected the stop bit. + */ + if (!i2c->last_msg || i2c->line_status & LINESTAT_STOP_BIT_DET) + hret = ISR_COMPLETE(0); + else + img_i2c_switch_mode(i2c, MODE_WAITSTOP); + } + + /* now we've finished using regs, handle transaction completion */ + if (hret & ISR_COMPLETE_M) { + int status = -(hret & ISR_STATUS_M); + + img_i2c_complete_transaction(i2c, status); + if (hret & ISR_FATAL_M) + img_i2c_switch_mode(i2c, MODE_FATAL); + } + + /* Enable interrupts (int_enable may be altered by changing mode) */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +/* Force a bus reset sequence and wait for it to complete */ +static int img_i2c_reset_bus(struct img_i2c *i2c) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&i2c->lock, flags); + reinit_completion(&i2c->msg_complete); + img_i2c_reset_start(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = wait_for_completion_timeout(&i2c->msg_complete, IMG_I2C_TIMEOUT); + if (ret == 0) + return -ETIMEDOUT; + return 0; +} + +static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct img_i2c *i2c = i2c_get_adapdata(adap); + bool atomic = false; + int i, ret; + + if (i2c->mode == MODE_SUSPEND) { + WARN(1, "refusing to service transaction in suspended state\n"); + return -EIO; + } + + if (i2c->mode == MODE_FATAL) + return -EIO; + + for (i = 0; i < num; i++) { + if (likely(msgs[i].len)) + continue; + /* + * 0 byte reads are not possible because the slave could try + * and pull the data line low, preventing a stop bit. + */ + if (unlikely(msgs[i].flags & I2C_M_RD)) + return -EIO; + /* + * 0 byte writes are possible and used for probing, but we + * cannot do them in automatic mode, so use atomic mode + * instead. + */ + atomic = true; + } + + ret = clk_prepare_enable(i2c->scb_clk); + if (ret) + return ret; + + for (i = 0; i < num; i++) { + struct i2c_msg *msg = &msgs[i]; + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + + /* + * Make a copy of the message struct. We mustn't modify the + * original or we'll confuse drivers and i2c-dev. + */ + i2c->msg = *msg; + i2c->msg_status = 0; + + /* + * After the last message we must have waited for a stop bit. + * Not waiting can cause problems when the clock is disabled + * before the stop bit is sent, and the linux I2C interface + * requires separate transfers not to joined with repeated + * start. + */ + i2c->last_msg = (i == num - 1); + reinit_completion(&i2c->msg_complete); + + if (atomic) + img_i2c_atomic_start(i2c); + else if (msg->flags & I2C_M_RD) + img_i2c_read(i2c); + else + img_i2c_write(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = wait_for_completion_timeout(&i2c->msg_complete, + IMG_I2C_TIMEOUT); + del_timer_sync(&i2c->check_timer); + + if (ret == 0) { + dev_err(adap->dev.parent, "i2c transfer timed out\n"); + i2c->msg_status = -ETIMEDOUT; + break; + } + + if (i2c->msg_status) + break; + } + + clk_disable_unprepare(i2c->scb_clk); + + return i2c->msg_status ? i2c->msg_status : num; +} + +static u32 img_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm img_i2c_algo = { + .master_xfer = img_i2c_xfer, + .functionality = img_i2c_func, +}; + +static int img_i2c_init(struct img_i2c *i2c) +{ + unsigned int clk_khz, bitrate_khz, clk_period, tckh, tckl, tsdh; + unsigned int i, ret, data, prescale, inc, int_bitrate, filt; + struct img_i2c_timings timing; + u32 rev; + + ret = clk_prepare_enable(i2c->scb_clk); + if (ret) + return ret; + + rev = img_i2c_readl(i2c, SCB_CORE_REV_REG); + if ((rev & 0x00ffffff) < 0x00020200) { + dev_info(i2c->adap.dev.parent, + "Unknown hardware revision (%d.%d.%d.%d)\n", + (rev >> 24) & 0xff, (rev >> 16) & 0xff, + (rev >> 8) & 0xff, rev & 0xff); + clk_disable_unprepare(i2c->scb_clk); + return -EINVAL; + } + + if (rev == REL_SOC_IP_SCB_2_2_1) { + i2c->need_wr_rd_fence = true; + dev_info(i2c->adap.dev.parent, "fence quirk enabled"); + } + + bitrate_khz = i2c->bitrate / 1000; + clk_khz = clk_get_rate(i2c->scb_clk) / 1000; + + /* Determine what mode we're in from the bitrate */ + timing = timings[0]; + for (i = 0; i < ARRAY_SIZE(timings); i++) { + if (i2c->bitrate <= timings[i].max_bitrate) { + timing = timings[i]; + break; + } + } + + /* Find the prescale that would give us that inc (approx delay = 0) */ + prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz); + prescale = clamp_t(unsigned int, prescale, 1, 8); + clk_khz /= prescale; + + /* Setup the clock increment value */ + inc = (256 * 16 * bitrate_khz) / clk_khz; + + /* + * The clock generation logic allows to filter glitches on the bus. + * This filter is able to remove bus glitches shorter than 50ns. + * If the clock enable rate is greater than 20 MHz, no filtering + * is required, so we need to disable it. + * If it's between the 20-40 MHz range, there's no need to divide + * the clock to get a filter. + */ + if (clk_khz < 20000) { + filt = SCB_FILT_DISABLE; + } else if (clk_khz < 40000) { + filt = SCB_FILT_BYPASS; + } else { + /* Calculate filter clock */ + filt = (64000 / ((clk_khz / 1000) * SCB_FILT_GLITCH)); + + /* Scale up if needed */ + if (64000 % ((clk_khz / 1000) * SCB_FILT_GLITCH)) + inc++; + + if (filt > SCB_FILT_INC_MASK) + filt = SCB_FILT_INC_MASK; + + filt = (filt & SCB_FILT_INC_MASK) << SCB_FILT_INC_SHIFT; + } + data = filt | ((inc & SCB_INC_MASK) << SCB_INC_SHIFT) | (prescale - 1); + img_i2c_writel(i2c, SCB_CLK_SET_REG, data); + + /* Obtain the clock period of the fx16 clock in ns */ + clk_period = (256 * 1000000) / (clk_khz * inc); + + /* Calculate the bitrate in terms of internal clock pulses */ + int_bitrate = 1000000 / (bitrate_khz * clk_period); + if ((1000000 % (bitrate_khz * clk_period)) >= + ((bitrate_khz * clk_period) / 2)) + int_bitrate++; + + /* Setup TCKH value */ + tckh = timing.tckh / clk_period; + if (timing.tckh % clk_period) + tckh++; + + if (tckh > 0) + data = tckh - 1; + else + data = 0; + + img_i2c_writel(i2c, SCB_TIME_TCKH_REG, data); + + /* Setup TCKL value */ + tckl = int_bitrate - tckh; + + if (tckl > 0) + data = tckl - 1; + else + data = 0; + + img_i2c_writel(i2c, SCB_TIME_TCKL_REG, data); + + /* Setup TSDH value */ + tsdh = timing.tsdh / clk_period; + if (timing.tsdh % clk_period) + tsdh++; + + if (tsdh > 1) + data = tsdh - 1; + else + data = 0x01; + img_i2c_writel(i2c, SCB_TIME_TSDH_REG, data); + + /* This value is used later */ + tsdh = data; + + /* Setup TPL value */ + data = timing.tpl / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TPL_REG, data); + + /* Setup TPH value */ + data = timing.tph / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TPH_REG, data); + + /* Setup TSDL value to TPL + TSDH + 2 */ + img_i2c_writel(i2c, SCB_TIME_TSDL_REG, data + tsdh + 2); + + /* Setup TP2S value */ + data = timing.tp2s / clk_period; + if (data > 0) + --data; + img_i2c_writel(i2c, SCB_TIME_TP2S_REG, data); + + img_i2c_writel(i2c, SCB_TIME_TBI_REG, TIMEOUT_TBI); + img_i2c_writel(i2c, SCB_TIME_TSL_REG, TIMEOUT_TSL); + img_i2c_writel(i2c, SCB_TIME_TDL_REG, TIMEOUT_TDL); + + /* Take module out of soft reset and enable clocks */ + img_i2c_soft_reset(i2c); + + /* Disable all interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, 0); + + /* Clear all interrupts */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); + + /* Clear the scb_line_status events */ + img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + + /* Enable interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + + /* Perform a synchronous sequence to reset the bus */ + ret = img_i2c_reset_bus(i2c); + + clk_disable_unprepare(i2c->scb_clk); + + return ret; +} + +static int img_i2c_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct img_i2c *i2c; + struct resource *res; + int irq, ret; + u32 val; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct img_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "can't get irq number\n"); + return irq; + } + + i2c->sys_clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(i2c->sys_clk)) { + dev_err(&pdev->dev, "can't get system clock\n"); + return PTR_ERR(i2c->sys_clk); + } + + i2c->scb_clk = devm_clk_get(&pdev->dev, "scb"); + if (IS_ERR(i2c->scb_clk)) { + dev_err(&pdev->dev, "can't get core clock\n"); + return PTR_ERR(i2c->scb_clk); + } + + ret = devm_request_irq(&pdev->dev, irq, img_i2c_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "can't request irq %d\n", irq); + return ret; + } + + /* Set up the exception check timer */ + init_timer(&i2c->check_timer); + i2c->check_timer.function = img_i2c_check_timer; + i2c->check_timer.data = (unsigned long)i2c; + + i2c->bitrate = timings[0].max_bitrate; + if (!of_property_read_u32(node, "clock-frequency", &val)) + i2c->bitrate = val; + + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = node; + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &img_i2c_algo; + i2c->adap.retries = 5; + i2c->adap.nr = pdev->id; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "IMG SCB I2C"); + + img_i2c_switch_mode(i2c, MODE_INACTIVE); + spin_lock_init(&i2c->lock); + init_completion(&i2c->msg_complete); + + platform_set_drvdata(pdev, i2c); + + ret = clk_prepare_enable(i2c->sys_clk); + if (ret) + return ret; + + ret = img_i2c_init(i2c); + if (ret) + goto disable_clk; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add adapter\n"); + goto disable_clk; + } + + return 0; + +disable_clk: + clk_disable_unprepare(i2c->sys_clk); + return ret; +} + +static int img_i2c_remove(struct platform_device *dev) +{ + struct img_i2c *i2c = platform_get_drvdata(dev); + + i2c_del_adapter(&i2c->adap); + clk_disable_unprepare(i2c->sys_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2c_suspend(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + + img_i2c_switch_mode(i2c, MODE_SUSPEND); + + clk_disable_unprepare(i2c->sys_clk); + + return 0; +} + +static int img_i2c_resume(struct device *dev) +{ + struct img_i2c *i2c = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2c->sys_clk); + if (ret) + return ret; + + img_i2c_init(i2c); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(img_i2c_pm, img_i2c_suspend, img_i2c_resume); + +static const struct of_device_id img_scb_i2c_match[] = { + { .compatible = "img,scb-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(of, img_scb_i2c_match); + +static struct platform_driver img_scb_i2c_driver = { + .driver = { + .name = "img-i2c-scb", + .of_match_table = img_scb_i2c_match, + .pm = &img_i2c_pm, + }, + .probe = img_i2c_probe, + .remove = img_i2c_remove, +}; +module_platform_driver(img_scb_i2c_driver); + +MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>"); +MODULE_DESCRIPTION("IMG host I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index e9fb7cf78612..aab1f4bb9e30 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -32,22 +32,27 @@ /** Includes ******************************************************************* *******************************************************************************/ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/completion.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/dmapool.h> +#include <linux/err.h> +#include <linux/errno.h> #include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/sched.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_dma.h> #include <linux/platform_data/i2c-imx.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> /** Defines ******************************************************************** *******************************************************************************/ @@ -58,6 +63,15 @@ /* Default value */ #define IMX_I2C_BIT_RATE 100000 /* 100kHz */ +/* + * Enable DMA if transfer byte size is bigger than this threshold. + * As the hardware request, it must bigger than 4 bytes.\ + * I have set '16' here, maybe it's not the best but I think it's + * the appropriate. + */ +#define DMA_THRESHOLD 16 +#define DMA_TIMEOUT 1000 + /* IMX I2C registers: * the I2C register offset is different between SoCs, * to provid support for all these chips, split the @@ -83,6 +97,7 @@ #define I2SR_IBB 0x20 #define I2SR_IAAS 0x40 #define I2SR_ICF 0x80 +#define I2CR_DMAEN 0x02 #define I2CR_RSTA 0x04 #define I2CR_TXAK 0x08 #define I2CR_MTX 0x10 @@ -169,6 +184,17 @@ struct imx_i2c_hwdata { unsigned i2cr_ien_opcode; }; +struct imx_i2c_dma { + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct dma_chan *chan_using; + struct completion cmd_complete; + dma_addr_t dma_buf; + unsigned int dma_len; + enum dma_transfer_direction dma_transfer_dir; + enum dma_data_direction dma_data_dir; +}; + struct imx_i2c_struct { struct i2c_adapter adapter; struct clk *clk; @@ -181,6 +207,8 @@ struct imx_i2c_struct { unsigned int cur_clk; unsigned int bitrate; const struct imx_i2c_hwdata *hwdata; + + struct imx_i2c_dma *dma; }; static const struct imx_i2c_hwdata imx1_i2c_hwdata = { @@ -251,6 +279,138 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift)); } +/* Functions for DMA support */ +static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, + dma_addr_t phy_addr) +{ + struct imx_i2c_dma *dma; + struct dma_slave_config dma_sconfig; + struct device *dev = &i2c_imx->adapter.dev; + int ret; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return; + + dma->chan_tx = dma_request_slave_channel(dev, "tx"); + if (!dma->chan_tx) { + dev_dbg(dev, "can't request DMA tx channel\n"); + ret = -ENODEV; + goto fail_al; + } + + dma_sconfig.dst_addr = phy_addr + + (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.dst_maxburst = 1; + dma_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); + if (ret < 0) { + dev_dbg(dev, "can't configure tx channel\n"); + goto fail_tx; + } + + dma->chan_rx = dma_request_slave_channel(dev, "rx"); + if (!dma->chan_rx) { + dev_dbg(dev, "can't request DMA rx channel\n"); + ret = -ENODEV; + goto fail_tx; + } + + dma_sconfig.src_addr = phy_addr + + (IMX_I2C_I2DR << i2c_imx->hwdata->regshift); + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 1; + dma_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); + if (ret < 0) { + dev_dbg(dev, "can't configure rx channel\n"); + goto fail_rx; + } + + i2c_imx->dma = dma; + init_completion(&dma->cmd_complete); + dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); + + return; + +fail_rx: + dma_release_channel(dma->chan_rx); +fail_tx: + dma_release_channel(dma->chan_tx); +fail_al: + devm_kfree(dev, dma); + dev_info(dev, "can't use DMA\n"); +} + +static void i2c_imx_dma_callback(void *arg) +{ + struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg; + struct imx_i2c_dma *dma = i2c_imx->dma; + + dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf, + dma->dma_len, dma->dma_data_dir); + complete(&dma->cmd_complete); +} + +static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs) +{ + struct imx_i2c_dma *dma = i2c_imx->dma; + struct dma_async_tx_descriptor *txdesc; + struct device *dev = &i2c_imx->adapter.dev; + struct device *chan_dev = dma->chan_using->device->dev; + + dma->dma_buf = dma_map_single(chan_dev, msgs->buf, + dma->dma_len, dma->dma_data_dir); + if (dma_mapping_error(chan_dev, dma->dma_buf)) { + dev_err(dev, "DMA mapping failed\n"); + goto err_map; + } + + txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf, + dma->dma_len, dma->dma_transfer_dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_err(dev, "Not able to get desc for DMA xfer\n"); + goto err_desc; + } + + txdesc->callback = i2c_imx_dma_callback; + txdesc->callback_param = i2c_imx; + if (dma_submit_error(dmaengine_submit(txdesc))) { + dev_err(dev, "DMA submit failed\n"); + goto err_submit; + } + + dma_async_issue_pending(dma->chan_using); + return 0; + +err_submit: +err_desc: + dma_unmap_single(chan_dev, dma->dma_buf, + dma->dma_len, dma->dma_data_dir); +err_map: + return -EINVAL; +} + +static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) +{ + struct imx_i2c_dma *dma = i2c_imx->dma; + + dma->dma_buf = 0; + dma->dma_len = 0; + + dma_release_channel(dma->chan_tx); + dma->chan_tx = NULL; + + dma_release_channel(dma->chan_rx); + dma->chan_rx = NULL; + + dma->chan_using = NULL; +} + /** Functions for IMX I2C adapter driver *************************************** *******************************************************************************/ @@ -382,6 +542,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) i2c_imx->stopped = 0; temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; + temp &= ~I2CR_DMAEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); return result; } @@ -395,6 +556,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); + if (i2c_imx->dma) + temp &= ~I2CR_DMAEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); } if (is_imx1_i2c(i2c_imx)) { @@ -435,6 +598,155 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) return IRQ_NONE; } +static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs) +{ + int result; + unsigned int temp = 0; + unsigned long orig_jiffies = jiffies; + struct imx_i2c_dma *dma = i2c_imx->dma; + struct device *dev = &i2c_imx->adapter.dev; + + dma->chan_using = dma->chan_tx; + dma->dma_transfer_dir = DMA_MEM_TO_DEV; + dma->dma_data_dir = DMA_TO_DEVICE; + dma->dma_len = msgs->len - 1; + result = i2c_imx_dma_xfer(i2c_imx, msgs); + if (result) + return result; + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* + * Write slave address. + * The first byte must be transmitted by the CPU. + */ + imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR); + reinit_completion(&i2c_imx->dma->cmd_complete); + result = wait_for_completion_timeout( + &i2c_imx->dma->cmd_complete, + msecs_to_jiffies(DMA_TIMEOUT)); + if (result <= 0) { + dmaengine_terminate_all(dma->chan_using); + return result ?: -ETIMEDOUT; + } + + /* Waiting for transfer complete. */ + while (1) { + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if (temp & I2SR_ICF) + break; + if (time_after(jiffies, orig_jiffies + + msecs_to_jiffies(DMA_TIMEOUT))) { + dev_dbg(dev, "<%s> Timeout\n", __func__); + return -ETIMEDOUT; + } + schedule(); + } + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp &= ~I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* The last data byte must be transferred by the CPU. */ + imx_i2c_write_reg(msgs->buf[msgs->len-1], + i2c_imx, IMX_I2C_I2DR); + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + + return i2c_imx_acked(i2c_imx); +} + +static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, + struct i2c_msg *msgs, bool is_lastmsg) +{ + int result; + unsigned int temp; + unsigned long orig_jiffies = jiffies; + struct imx_i2c_dma *dma = i2c_imx->dma; + struct device *dev = &i2c_imx->adapter.dev; + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + dma->chan_using = dma->chan_rx; + dma->dma_transfer_dir = DMA_DEV_TO_MEM; + dma->dma_data_dir = DMA_FROM_DEVICE; + /* The last two data bytes must be transferred by the CPU. */ + dma->dma_len = msgs->len - 2; + result = i2c_imx_dma_xfer(i2c_imx, msgs); + if (result) + return result; + + reinit_completion(&i2c_imx->dma->cmd_complete); + result = wait_for_completion_timeout( + &i2c_imx->dma->cmd_complete, + msecs_to_jiffies(DMA_TIMEOUT)); + if (result <= 0) { + dmaengine_terminate_all(dma->chan_using); + return result ?: -ETIMEDOUT; + } + + /* waiting for transfer complete. */ + while (1) { + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if (temp & I2SR_ICF) + break; + if (time_after(jiffies, orig_jiffies + + msecs_to_jiffies(DMA_TIMEOUT))) { + dev_dbg(dev, "<%s> Timeout\n", __func__); + return -ETIMEDOUT; + } + schedule(); + } + + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp &= ~I2CR_DMAEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + /* read n-1 byte data */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + /* read n byte data */ + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + + if (is_lastmsg) { + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + dev_dbg(dev, "<%s> clear MSTA\n", __func__); + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx->stopped = 1; + } else { + /* + * For i2c master receiver repeat restart operation like: + * read -> repeat MSTA -> read/write + * The controller must set MTX before read the last byte in + * the first read operation, otherwise the first read cost + * one extra clock cycle. + */ + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp |= I2CR_MTX; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + } + msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + + return 0; +} + static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) { int i, result; @@ -504,6 +816,9 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); + if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) + return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); + /* read data */ for (i = 0; i < msgs->len; i++) { u8 len = 0; @@ -618,8 +933,12 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, #endif if (msgs[i].flags & I2C_M_RD) result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); - else - result = i2c_imx_write(i2c_imx, &msgs[i]); + else { + if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) + result = i2c_imx_dma_write(i2c_imx, &msgs[i]); + else + result = i2c_imx_write(i2c_imx, &msgs[i]); + } if (result) goto fail0; } @@ -654,6 +973,7 @@ static int i2c_imx_probe(struct platform_device *pdev) struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); void __iomem *base; int irq, ret; + dma_addr_t phy_addr; dev_dbg(&pdev->dev, "<%s>\n", __func__); @@ -668,8 +988,8 @@ static int i2c_imx_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct), - GFP_KERNEL); + phy_addr = (dma_addr_t)res->start; + i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL); if (!i2c_imx) return -ENOMEM; @@ -743,6 +1063,9 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.name); dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); + /* Init DMA config if support*/ + i2c_imx_dma_request(i2c_imx, phy_addr); + return 0; /* Return OK */ clk_disable: @@ -758,6 +1081,9 @@ static int i2c_imx_remove(struct platform_device *pdev) dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); i2c_del_adapter(&i2c_imx->adapter); + if (i2c_imx->dma) + i2c_imx_dma_free(i2c_imx); + /* setup chip registers to defaults */ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR); imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR); diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c new file mode 100644 index 000000000000..5e176adca8e8 --- /dev/null +++ b/drivers/i2c/busses/i2c-meson.c @@ -0,0 +1,492 @@ +/* + * I2C bus driver for Amlogic Meson SoCs + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +/* Meson I2C register map */ +#define REG_CTRL 0x00 +#define REG_SLAVE_ADDR 0x04 +#define REG_TOK_LIST0 0x08 +#define REG_TOK_LIST1 0x0c +#define REG_TOK_WDATA0 0x10 +#define REG_TOK_WDATA1 0x14 +#define REG_TOK_RDATA0 0x18 +#define REG_TOK_RDATA1 0x1c + +/* Control register fields */ +#define REG_CTRL_START BIT(0) +#define REG_CTRL_ACK_IGNORE BIT(1) +#define REG_CTRL_STATUS BIT(2) +#define REG_CTRL_ERROR BIT(3) +#define REG_CTRL_CLKDIV_SHIFT 12 +#define REG_CTRL_CLKDIV_MASK ((BIT(10) - 1) << REG_CTRL_CLKDIV_SHIFT) + +#define I2C_TIMEOUT_MS 500 +#define DEFAULT_FREQ 100000 + +enum { + TOKEN_END = 0, + TOKEN_START, + TOKEN_SLAVE_ADDR_WRITE, + TOKEN_SLAVE_ADDR_READ, + TOKEN_DATA, + TOKEN_DATA_LAST, + TOKEN_STOP, +}; + +enum { + STATE_IDLE, + STATE_READ, + STATE_WRITE, + STATE_STOP, +}; + +/** + * struct meson_i2c - Meson I2C device private data + * + * @adap: I2C adapter instance + * @dev: Pointer to device structure + * @regs: Base address of the device memory mapped registers + * @clk: Pointer to clock structure + * @irq: IRQ number + * @msg: Pointer to the current I2C message + * @state: Current state in the driver state machine + * @last: Flag set for the last message in the transfer + * @count: Number of bytes to be sent/received in current transfer + * @pos: Current position in the send/receive buffer + * @error: Flag set when an error is received + * @lock: To avoid race conditions between irq handler and xfer code + * @done: Completion used to wait for transfer termination + * @frequency: Operating frequency of I2C bus clock + * @tokens: Sequence of tokens to be written to the device + * @num_tokens: Number of tokens + */ +struct meson_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *regs; + struct clk *clk; + int irq; + + struct i2c_msg *msg; + int state; + bool last; + int count; + int pos; + int error; + + spinlock_t lock; + struct completion done; + unsigned int frequency; + u32 tokens[2]; + int num_tokens; +}; + +static void meson_i2c_set_mask(struct meson_i2c *i2c, int reg, u32 mask, + u32 val) +{ + u32 data; + + data = readl(i2c->regs + reg); + data &= ~mask; + data |= val & mask; + writel(data, i2c->regs + reg); +} + +static void meson_i2c_reset_tokens(struct meson_i2c *i2c) +{ + i2c->tokens[0] = 0; + i2c->tokens[1] = 0; + i2c->num_tokens = 0; +} + +static void meson_i2c_add_token(struct meson_i2c *i2c, int token) +{ + if (i2c->num_tokens < 8) + i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4); + else + i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4); + + i2c->num_tokens++; +} + +static void meson_i2c_write_tokens(struct meson_i2c *i2c) +{ + writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0); + writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1); +} + +static void meson_i2c_set_clk_div(struct meson_i2c *i2c) +{ + unsigned long clk_rate = clk_get_rate(i2c->clk); + unsigned int div; + + div = DIV_ROUND_UP(clk_rate, i2c->frequency * 4); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK, + div << REG_CTRL_CLKDIV_SHIFT); + + dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__, + clk_rate, i2c->frequency, div); +} + +static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len) +{ + u32 rdata0, rdata1; + int i; + + rdata0 = readl(i2c->regs + REG_TOK_RDATA0); + rdata1 = readl(i2c->regs + REG_TOK_RDATA1); + + dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__, + rdata0, rdata1, len); + + for (i = 0; i < min_t(int, 4, len); i++) + *buf++ = (rdata0 >> i * 8) & 0xff; + + for (i = 4; i < min_t(int, 8, len); i++) + *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; +} + +static void meson_i2c_put_data(struct meson_i2c *i2c, char *buf, int len) +{ + u32 wdata0 = 0, wdata1 = 0; + int i; + + for (i = 0; i < min_t(int, 4, len); i++) + wdata0 |= *buf++ << (i * 8); + + for (i = 4; i < min_t(int, 8, len); i++) + wdata1 |= *buf++ << ((i - 4) * 8); + + writel(wdata0, i2c->regs + REG_TOK_WDATA0); + writel(wdata0, i2c->regs + REG_TOK_WDATA1); + + dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__, + wdata0, wdata1, len); +} + +static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) +{ + bool write = !(i2c->msg->flags & I2C_M_RD); + int i; + + i2c->count = min_t(int, i2c->msg->len - i2c->pos, 8); + + for (i = 0; i < i2c->count - 1; i++) + meson_i2c_add_token(i2c, TOKEN_DATA); + + if (i2c->count) { + if (write || i2c->pos + i2c->count < i2c->msg->len) + meson_i2c_add_token(i2c, TOKEN_DATA); + else + meson_i2c_add_token(i2c, TOKEN_DATA_LAST); + } + + if (write) + meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count); +} + +static void meson_i2c_stop(struct meson_i2c *i2c) +{ + dev_dbg(i2c->dev, "%s: last %d\n", __func__, i2c->last); + + if (i2c->last) { + i2c->state = STATE_STOP; + meson_i2c_add_token(i2c, TOKEN_STOP); + } else { + i2c->state = STATE_IDLE; + complete_all(&i2c->done); + } +} + +static irqreturn_t meson_i2c_irq(int irqno, void *dev_id) +{ + struct meson_i2c *i2c = dev_id; + unsigned int ctrl; + + spin_lock(&i2c->lock); + + meson_i2c_reset_tokens(i2c); + ctrl = readl(i2c->regs + REG_CTRL); + + dev_dbg(i2c->dev, "irq: state %d, pos %d, count %d, ctrl %08x\n", + i2c->state, i2c->pos, i2c->count, ctrl); + + if (ctrl & REG_CTRL_ERROR && i2c->state != STATE_IDLE) { + /* + * The bit is set when the IGNORE_NAK bit is cleared + * and the device didn't respond. In this case, the + * I2C controller automatically generates a STOP + * condition. + */ + dev_dbg(i2c->dev, "error bit set\n"); + i2c->error = -ENXIO; + i2c->state = STATE_IDLE; + complete_all(&i2c->done); + goto out; + } + + switch (i2c->state) { + case STATE_READ: + if (i2c->count > 0) { + meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos, + i2c->count); + i2c->pos += i2c->count; + } + + if (i2c->pos >= i2c->msg->len) { + meson_i2c_stop(i2c); + break; + } + + meson_i2c_prepare_xfer(i2c); + break; + case STATE_WRITE: + i2c->pos += i2c->count; + + if (i2c->pos >= i2c->msg->len) { + meson_i2c_stop(i2c); + break; + } + + meson_i2c_prepare_xfer(i2c); + break; + case STATE_STOP: + i2c->state = STATE_IDLE; + complete_all(&i2c->done); + break; + case STATE_IDLE: + break; + } + +out: + if (i2c->state != STATE_IDLE) { + /* Restart the processing */ + meson_i2c_write_tokens(i2c); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, + REG_CTRL_START); + } + + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg) +{ + int token; + + token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : + TOKEN_SLAVE_ADDR_WRITE; + + writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR); + meson_i2c_add_token(i2c, TOKEN_START); + meson_i2c_add_token(i2c, token); +} + +static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg, + int last) +{ + unsigned long time_left, flags; + int ret = 0; + + i2c->msg = msg; + i2c->last = last; + i2c->pos = 0; + i2c->count = 0; + i2c->error = 0; + + meson_i2c_reset_tokens(i2c); + + flags = (msg->flags & I2C_M_IGNORE_NAK) ? REG_CTRL_ACK_IGNORE : 0; + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_ACK_IGNORE, flags); + + if (!(msg->flags & I2C_M_NOSTART)) + meson_i2c_do_start(i2c, msg); + + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + meson_i2c_prepare_xfer(i2c); + meson_i2c_write_tokens(i2c); + reinit_completion(&i2c->done); + + /* Start the transfer */ + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START); + + time_left = msecs_to_jiffies(I2C_TIMEOUT_MS); + time_left = wait_for_completion_timeout(&i2c->done, time_left); + + /* + * Protect access to i2c struct and registers from interrupt + * handlers triggered by a transfer terminated after the + * timeout period + */ + spin_lock_irqsave(&i2c->lock, flags); + + /* Abort any active operation */ + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); + + if (!time_left) { + i2c->state = STATE_IDLE; + ret = -ETIMEDOUT; + } + + if (i2c->error) + ret = i2c->error; + + spin_unlock_irqrestore(&i2c->lock, flags); + + return ret; +} + +static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct meson_i2c *i2c = adap->algo_data; + int i, ret = 0, count = 0; + + clk_enable(i2c->clk); + meson_i2c_set_clk_div(i2c); + + for (i = 0; i < num; i++) { + ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1); + if (ret) + break; + count++; + } + + clk_disable(i2c->clk); + + return ret ? ret : count; +} + +static u32 meson_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm meson_i2c_algorithm = { + .master_xfer = meson_i2c_xfer, + .functionality = meson_i2c_func, +}; + +static int meson_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct meson_i2c *i2c; + struct resource *mem; + int ret = 0; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct meson_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &i2c->frequency)) + i2c->frequency = DEFAULT_FREQ; + + i2c->dev = &pdev->dev; + platform_set_drvdata(pdev, i2c); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->done); + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "can't get device clock\n"); + return PTR_ERR(i2c->clk); + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(i2c->regs)) + return PTR_ERR(i2c->regs); + + i2c->irq = platform_get_irq(pdev, 0); + if (i2c->irq < 0) { + dev_err(&pdev->dev, "can't find IRQ\n"); + return i2c->irq; + } + + ret = devm_request_irq(&pdev->dev, i2c->irq, meson_i2c_irq, + 0, dev_name(&pdev->dev), i2c); + if (ret < 0) { + dev_err(&pdev->dev, "can't request IRQ\n"); + return ret; + } + + ret = clk_prepare(i2c->clk); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare clock\n"); + return ret; + } + + strlcpy(i2c->adap.name, "Meson I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &meson_i2c_algorithm; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + + /* + * A transfer is triggered when START bit changes from 0 to 1. + * Ensure that the bit is set to 0 after probe + */ + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "can't register adapter\n"); + clk_unprepare(i2c->clk); + return ret; + } + + return 0; +} + +static int meson_i2c_remove(struct platform_device *pdev) +{ + struct meson_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + clk_unprepare(i2c->clk); + + return 0; +} + +static const struct of_device_id meson_i2c_match[] = { + { .compatible = "amlogic,meson6-i2c" }, + { }, +}; + +static struct platform_driver meson_i2c_driver = { + .probe = meson_i2c_probe, + .remove = meson_i2c_remove, + .driver = { + .name = "meson-i2c", + .of_match_table = meson_i2c_match, + }, +}; + +module_platform_driver(meson_i2c_driver); + +MODULE_DESCRIPTION("Amlogic Meson I2C Bus driver"); +MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 0edf630b099a..4c5d7d92b9fe 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -124,7 +124,7 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c) static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) { unsigned long orig_jiffies = jiffies; - u32 x; + u32 cmd_err; int result = 0; if (!i2c->irq) { @@ -133,11 +133,11 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) if (time_after(jiffies, orig_jiffies + timeout)) { dev_dbg(i2c->dev, "timeout\n"); writeccr(i2c, 0); - result = -EIO; + result = -ETIMEDOUT; break; } } - x = readb(i2c->base + MPC_I2C_SR); + cmd_err = readb(i2c->base + MPC_I2C_SR); writeb(0, i2c->base + MPC_I2C_SR); } else { /* Interrupt mode */ @@ -150,28 +150,28 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) result = -ETIMEDOUT; } - x = i2c->interrupt; + cmd_err = i2c->interrupt; i2c->interrupt = 0; } if (result < 0) return result; - if (!(x & CSR_MCF)) { + if (!(cmd_err & CSR_MCF)) { dev_dbg(i2c->dev, "unfinished\n"); return -EIO; } - if (x & CSR_MAL) { + if (cmd_err & CSR_MAL) { dev_dbg(i2c->dev, "MAL\n"); - return -EIO; + return -EAGAIN; } - if (writing && (x & CSR_RXAK)) { + if (writing && (cmd_err & CSR_RXAK)) { dev_dbg(i2c->dev, "No RXAK\n"); /* generate stop */ writeccr(i2c, CCR_MEN); - return -EIO; + return -ENXIO; } return 0; } diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 07e1be6f8992..3e7893aa97ef 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -811,7 +811,7 @@ static int mxs_i2c_probe(struct platform_device *pdev) struct resource *res; int err, irq; - i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL); + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 0e650a0d0ad0..984fede623ea 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -54,6 +54,9 @@ /* timeout for pm runtime autosuspend */ #define OMAP_I2C_PM_TIMEOUT 1000 /* ms */ +/* timeout for making decision on bus free status */ +#define OMAP_I2C_BUS_FREE_TIMEOUT (msecs_to_jiffies(10)) + /* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */ enum { OMAP_I2C_REV_REG = 0, @@ -98,7 +101,7 @@ enum { #define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ #define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ #define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */ -#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */ +#define OMAP_I2C_STAT_BF (1 << 8) /* Bus Free */ #define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ #define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ #define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */ @@ -146,16 +149,20 @@ enum { #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) @@ -202,6 +209,9 @@ struct omap_i2c_dev { */ u32 rev; unsigned b_hw:1; /* bad h/w fixes */ + unsigned bb_valid:1; /* true when BB-bit reflects + * the I2C bus state + */ unsigned receiver:1; /* true when we're in receiver mode */ u16 iestate; /* Saved interrupt register */ u16 pscstate; @@ -290,6 +300,12 @@ static void __omap_i2c_init(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); /* + * NOTE: right after setting CON_EN, STAT_BB could be 0 while the + * bus is busy. It will be changed to 1 on the next IP FCLK clock. + * udelay(1) will be enough to fix that. + */ + + /* * Don't write to this register if the IE state is 0 as it can * cause deadlock. */ @@ -328,7 +344,12 @@ static int omap_i2c_reset(struct omap_i2c_dev *dev) /* SYSC register is cleared by the reset; rewrite it */ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, sysc); + if (dev->rev > OMAP_I2C_REV_ON_3430_3530) { + /* Schedule I2C-bus monitoring on the next transfer */ + dev->bb_valid = 0; + } } + return 0; } @@ -441,6 +462,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) dev->scllstate = scll; dev->sclhstate = sclh; + if (dev->rev <= OMAP_I2C_REV_ON_3430_3530) { + /* Not implemented */ + dev->bb_valid = 1; + } + __omap_i2c_init(dev); return 0; @@ -465,6 +491,91 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev) return 0; } +/* + * Wait while BB-bit doesn't reflect the I2C bus state + * + * In a multimaster environment, after IP software reset, BB-bit value doesn't + * correspond to the current bus state. It may happen what BB-bit will be 0, + * while the bus is busy due to another I2C master activity. + * Here are BB-bit values after reset: + * SDA SCL BB NOTES + * 0 0 0 1, 2 + * 1 0 0 1, 2 + * 0 1 1 + * 1 1 0 3 + * Later, if IP detect SDA=0 and SCL=1 (ACK) or SDA 1->0 while SCL=1 (START) + * combinations on the bus, it set BB-bit to 1. + * If IP detect SDA 0->1 while SCL=1 (STOP) combination on the bus, + * it set BB-bit to 0 and BF to 1. + * BB and BF bits correctly tracks the bus state while IP is suspended + * BB bit became valid on the next FCLK clock after CON_EN bit set + * + * NOTES: + * 1. Any transfer started when BB=0 and bus is busy wouldn't be + * completed by IP and results in controller timeout. + * 2. Any transfer started when BB=0 and SCL=0 results in IP + * starting to drive SDA low. In that case IP corrupt data + * on the bus. + * 3. Any transfer started in the middle of another master's transfer + * results in unpredictable results and data corruption + */ +static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *dev) +{ + unsigned long bus_free_timeout = 0; + unsigned long timeout; + int bus_free = 0; + u16 stat, systest; + + if (dev->bb_valid) + return 0; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (1) { + stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + /* + * We will see BB or BF event in a case IP had detected any + * activity on the I2C bus. Now IP correctly tracks the bus + * state. BB-bit value is valid. + */ + if (stat & (OMAP_I2C_STAT_BB | OMAP_I2C_STAT_BF)) + break; + + /* + * Otherwise, we must look signals on the bus to make + * the right decision. + */ + systest = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) && + (systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) { + if (!bus_free) { + bus_free_timeout = jiffies + + OMAP_I2C_BUS_FREE_TIMEOUT; + bus_free = 1; + } + + /* + * SDA and SCL lines was high for 10 ms without bus + * activity detected. The bus is free. Consider + * BB-bit value is valid. + */ + if (time_after(jiffies, bus_free_timeout)) + break; + } else { + bus_free = 0; + } + + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + + msleep(1); + } + + dev->bb_valid = 1; + return 0; +} + static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx) { u16 buf; @@ -557,7 +668,11 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, if (!dev->b_hw && stop) w |= OMAP_I2C_CON_STP; - + /* + * NOTE: STAT_BB bit could became 1 here if another master occupy + * the bus. IP successfully complete transfer when the bus will be + * free again (BB reset to 0). + */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); /* @@ -600,13 +715,15 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, return 0; /* We have an error */ - if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | - OMAP_I2C_STAT_XUDF)) { + if (dev->cmd_err & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF)) { omap_i2c_reset(dev); __omap_i2c_init(dev); return -EIO; } + if (dev->cmd_err & OMAP_I2C_STAT_AL) + return -EAGAIN; + if (dev->cmd_err & OMAP_I2C_STAT_NACK) { if (msg->flags & I2C_M_IGNORE_NAK) return 0; @@ -635,6 +752,10 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) if (r < 0) goto out; + r = omap_i2c_wait_for_bb_valid(dev); + if (r < 0) + goto out; + r = omap_i2c_wait_for_bb(dev); if (r < 0) goto out; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index be671f7a0e06..f80df8f84609 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -885,7 +885,9 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) return; /* ignore */ } - if (isr & ISR_BED) { + if ((isr & ISR_BED) && + (!((i2c->msg->flags & I2C_M_IGNORE_NAK) && + (isr & ISR_ACKNAK)))) { int ret = BUS_ERROR; /* @@ -919,12 +921,14 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) icr |= ICR_ALDIE | ICR_TB; /* - * If this is the last byte of the last message, send - * a STOP. + * If this is the last byte of the last message or last byte + * of any message with I2C_M_STOP (e.g. SCCB), send a STOP. */ - if (i2c->msg_ptr == i2c->msg->len && - i2c->msg_idx == i2c->msg_num - 1) - icr |= ICR_STOP; + if ((i2c->msg_ptr == i2c->msg->len) && + ((i2c->msg->flags & I2C_M_STOP) || + (i2c->msg_idx == i2c->msg_num - 1))) + icr |= ICR_STOP; + } else if (i2c->msg_idx < i2c->msg_num - 1) { /* * Next segment of the message. @@ -1071,7 +1075,8 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num static u32 i2c_pxa_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART; } static const struct i2c_algorithm i2c_pxa_algorithm = { diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index d826e82dd997..835057741aa6 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -48,6 +48,12 @@ #define ICMAR 0x20 /* master address */ #define ICRXTX 0x24 /* data port */ +/* ICSCR */ +#define SDBS (1 << 3) /* slave data buffer select */ +#define SIE (1 << 2) /* slave interface enable */ +#define GCAE (1 << 1) /* general call address enable */ +#define FNA (1 << 0) /* forced non acknowledgment */ + /* ICMCR */ #define MDBS (1 << 7) /* non-fifo mode switch */ #define FSCL (1 << 6) /* override SCL pin */ @@ -58,6 +64,15 @@ #define FSB (1 << 1) /* force stop bit */ #define ESG (1 << 0) /* en startbit gen */ +/* ICSSR (also for ICSIER) */ +#define GCAR (1 << 6) /* general call received */ +#define STM (1 << 5) /* slave transmit mode */ +#define SSR (1 << 4) /* stop received */ +#define SDE (1 << 3) /* slave data empty */ +#define SDT (1 << 2) /* slave data transmitted */ +#define SDR (1 << 1) /* slave data received */ +#define SAR (1 << 0) /* slave addr received */ + /* ICMSR (also for ICMIE) */ #define MNR (1 << 6) /* nack received */ #define MAL (1 << 5) /* arbitration lost */ @@ -103,6 +118,7 @@ struct rcar_i2c_priv { u32 icccr; u32 flags; enum rcar_i2c_type devtype; + struct i2c_client *slave; }; #define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) @@ -126,15 +142,6 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg) static void rcar_i2c_init(struct rcar_i2c_priv *priv) { - /* - * reset slave mode. - * slave mode is not used on this driver - */ - rcar_i2c_write(priv, ICSIER, 0); - rcar_i2c_write(priv, ICSAR, 0); - rcar_i2c_write(priv, ICSCR, 0); - rcar_i2c_write(priv, ICSSR, 0); - /* reset master mode */ rcar_i2c_write(priv, ICMIER, 0); rcar_i2c_write(priv, ICMCR, 0); @@ -360,6 +367,63 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) return 0; } +static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) +{ + u32 ssr_raw, ssr_filtered; + u8 value; + + ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff; + ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER); + + if (!ssr_filtered) + return false; + + /* address detected */ + if (ssr_filtered & SAR) { + /* read or write request */ + if (ssr_raw & STM) { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR); + } else { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); + rcar_i2c_read(priv, ICRXTX); /* dummy read */ + rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR); + } + + rcar_i2c_write(priv, ICSSR, ~SAR & 0xff); + } + + /* master sent stop */ + if (ssr_filtered & SSR) { + i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value); + rcar_i2c_write(priv, ICSIER, SAR | SSR); + rcar_i2c_write(priv, ICSSR, ~SSR & 0xff); + } + + /* master wants to write to us */ + if (ssr_filtered & SDR) { + int ret; + + value = rcar_i2c_read(priv, ICRXTX); + ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value); + /* Send NACK in case of error */ + rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0)); + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); + rcar_i2c_write(priv, ICSSR, ~SDR & 0xff); + } + + /* master wants to read from us */ + if (ssr_filtered & SDE) { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSSR, ~SDE & 0xff); + } + + return true; +} + static irqreturn_t rcar_i2c_irq(int irq, void *ptr) { struct rcar_i2c_priv *priv = ptr; @@ -369,6 +433,9 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) /*-------------- spin lock -----------------*/ spin_lock(&priv->lock); + if (rcar_i2c_slave_irq(priv)) + goto exit; + msr = rcar_i2c_read(priv, ICMSR); /* Only handle interrupts that are currently enabled */ @@ -499,6 +566,43 @@ out: return ret; } +static int rcar_reg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + if (priv->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + pm_runtime_forbid(rcar_i2c_priv_to_dev(priv)); + + priv->slave = slave; + rcar_i2c_write(priv, ICSAR, slave->addr); + rcar_i2c_write(priv, ICSSR, 0); + rcar_i2c_write(priv, ICSIER, SAR | SSR); + rcar_i2c_write(priv, ICSCR, SIE | SDBS); + + return 0; +} + +static int rcar_unreg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + WARN_ON(!priv->slave); + + rcar_i2c_write(priv, ICSIER, 0); + rcar_i2c_write(priv, ICSCR, 0); + + priv->slave = NULL; + + pm_runtime_allow(rcar_i2c_priv_to_dev(priv)); + + return 0; +} + static u32 rcar_i2c_func(struct i2c_adapter *adap) { /* This HW can't do SMBUS_QUICK and NOSTART */ @@ -508,6 +612,8 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) static const struct i2c_algorithm rcar_i2c_algo = { .master_xfer = rcar_i2c_master_xfer, .functionality = rcar_i2c_func, + .reg_slave = rcar_reg_slave, + .unreg_slave = rcar_unreg_slave, }; static const struct of_device_id rcar_i2c_dt_ids[] = { diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index f486d0eac4d0..0ee5802f36d3 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -24,6 +24,7 @@ #include <linux/wait.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/math64.h> /* Register Map */ @@ -97,6 +98,7 @@ struct rk3x_i2c { /* Hardware resources */ void __iomem *regs; struct clk *clk; + struct notifier_block clk_rate_nb; /* Settings */ unsigned int scl_frequency; @@ -428,18 +430,231 @@ out: return IRQ_HANDLED; } -static void rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate) +/** + * Calculate divider values for desired SCL frequency + * + * @clk_rate: I2C input clock rate + * @scl_rate: Desired SCL rate + * @div_low: Divider output for low + * @div_high: Divider output for high + * + * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case + * a best-effort divider value is returned in divs. If the target rate is + * too high, we silently use the highest possible rate. + */ +static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, + unsigned long *div_low, unsigned long *div_high) { - unsigned long i2c_rate = clk_get_rate(i2c->clk); - unsigned int div; + unsigned long min_low_ns, min_high_ns; + unsigned long max_data_hold_ns; + unsigned long data_hold_buffer_ns; + unsigned long max_low_ns, min_total_ns; + + unsigned long clk_rate_khz, scl_rate_khz; + + unsigned long min_low_div, min_high_div; + unsigned long max_low_div; + + unsigned long min_div_for_hold, min_total_div; + unsigned long extra_div, extra_low_div, ideal_low_div; + + int ret = 0; + + /* Only support standard-mode and fast-mode */ + if (WARN_ON(scl_rate > 400000)) + scl_rate = 400000; + + /* prevent scl_rate_khz from becoming 0 */ + if (WARN_ON(scl_rate < 1000)) + scl_rate = 1000; + + /* + * min_low_ns: The minimum number of ns we need to hold low + * to meet i2c spec + * min_high_ns: The minimum number of ns we need to hold high + * to meet i2c spec + * max_low_ns: The maximum number of ns we can hold low + * to meet i2c spec + * + * Note: max_low_ns should be (max data hold time * 2 - buffer) + * This is because the i2c host on Rockchip holds the data line + * for half the low time. + */ + if (scl_rate <= 100000) { + min_low_ns = 4700; + min_high_ns = 4000; + max_data_hold_ns = 3450; + data_hold_buffer_ns = 50; + } else { + min_low_ns = 1300; + min_high_ns = 600; + max_data_hold_ns = 900; + data_hold_buffer_ns = 50; + } + max_low_ns = max_data_hold_ns * 2 - data_hold_buffer_ns; + min_total_ns = min_low_ns + min_high_ns; + + /* Adjust to avoid overflow */ + clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = scl_rate / 1000; + + /* + * We need the total div to be >= this number + * so we don't clock too fast. + */ + min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + /* These are the min dividers needed for min hold times. */ + min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + min_div_for_hold = (min_low_div + min_high_div); + + /* + * This is the maximum divider so we don't go over the max. + * We don't round up here (we round down) since this is a max. + */ + max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000); + + if (min_low_div > max_low_div) { + WARN_ONCE(true, + "Conflicting, min_low_div %lu, max_low_div %lu\n", + min_low_div, max_low_div); + max_low_div = min_low_div; + } + + if (min_div_for_hold > min_total_div) { + /* + * Time needed to meet hold requirements is important. + * Just use that. + */ + *div_low = min_low_div; + *div_high = min_high_div; + } else { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + */ + extra_div = min_total_div - min_div_for_hold; + + /* + * We'll try to split things up perfectly evenly, + * biasing slightly towards having a higher div + * for low (spend more time low). + */ + ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, + scl_rate_khz * 8 * min_total_ns); + + /* Don't allow it to go over the max */ + if (ideal_low_div > max_low_div) + ideal_low_div = max_low_div; + + /* + * Handle when the ideal low div is going to take up + * more than we have. + */ + if (ideal_low_div > min_low_div + extra_div) + ideal_low_div = min_low_div + extra_div; - /* set DIV = DIVH = DIVL - * SCL rate = (clk rate) / (8 * (DIVH + 1 + DIVL + 1)) - * = (clk rate) / (16 * (DIV + 1)) + /* Give low the "ideal" and give high whatever extra is left */ + extra_low_div = ideal_low_div - min_low_div; + *div_low = ideal_low_div; + *div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * Adjust to the fact that the hardware has an implicit "+1". + * NOTE: Above calculations always produce div_low > 0 and div_high > 0. */ - div = DIV_ROUND_UP(i2c_rate, scl_rate * 16) - 1; + *div_low = *div_low - 1; + *div_high = *div_high - 1; + + /* Maximum divider supported by hw is 0xffff */ + if (*div_low > 0xffff) { + *div_low = 0xffff; + ret = -EINVAL; + } + + if (*div_high > 0xffff) { + *div_high = 0xffff; + ret = -EINVAL; + } + + return ret; +} + +static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) +{ + unsigned long div_low, div_high; + u64 t_low_ns, t_high_ns; + int ret; + + ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, &div_low, + &div_high); + + WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency); + + clk_enable(i2c->clk); + i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV); + clk_disable(i2c->clk); - i2c_writel(i2c, (div << 16) | (div & 0xffff), REG_CLKDIV); + t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate); + t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate); + dev_dbg(i2c->dev, + "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n", + clk_rate / 1000, + 1000000000 / i2c->scl_frequency, + t_low_ns, t_high_ns); +} + +/** + * rk3x_i2c_clk_notifier_cb - Clock rate change callback + * @nb: Pointer to notifier block + * @event: Notification reason + * @data: Pointer to notification data object + * + * The callback checks whether a valid bus frequency can be generated after the + * change. If so, the change is acknowledged, otherwise the change is aborted. + * New dividers are written to the HW in the pre- or post change notification + * depending on the scaling direction. + * + * Code adapted from i2c-cadence.c. + * + * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK + * to acknowedge the change, NOTIFY_DONE if the notification is + * considered irrelevant. + */ +static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long + event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb); + unsigned long div_low, div_high; + + switch (event) { + case PRE_RATE_CHANGE: + if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency, + &div_low, &div_high) != 0) { + return NOTIFY_STOP; + } + + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->new_rate); + + return NOTIFY_OK; + case POST_RATE_CHANGE: + /* scale down */ + if (ndata->new_rate < ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->new_rate); + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + /* scale up */ + if (ndata->new_rate > ndata->old_rate) + rk3x_i2c_adapt_div(i2c, ndata->old_rate); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } } /** @@ -536,9 +751,6 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, clk_enable(i2c->clk); - /* The clock rate might have changed, so setup the divider again */ - rk3x_i2c_set_scl_rate(i2c, i2c->scl_frequency); - i2c->is_last_msg = false; /* @@ -624,6 +836,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev) int bus_nr; u32 value; int irq; + unsigned long clk_rate; i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL); if (!i2c) @@ -724,16 +937,28 @@ static int rk3x_i2c_probe(struct platform_device *pdev) return ret; } + i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb; + ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb); + if (ret != 0) { + dev_err(&pdev->dev, "Unable to register clock notifier\n"); + goto err_clk; + } + + clk_rate = clk_get_rate(i2c->clk); + rk3x_i2c_adapt_div(i2c, clk_rate); + ret = i2c_add_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "Could not register adapter\n"); - goto err_clk; + goto err_clk_notifier; } dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs); return 0; +err_clk_notifier: + clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); err_clk: clk_unprepare(i2c->clk); return ret; @@ -744,6 +969,8 @@ static int rk3x_i2c_remove(struct platform_device *pdev) struct rk3x_i2c *i2c = platform_get_drvdata(pdev); i2c_del_adapter(&i2c->adap); + + clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); clk_unprepare(i2c->clk); return 0; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 65244774bfa3..09a6bace457e 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -35,6 +35,8 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <asm/irq.h> @@ -87,6 +89,9 @@ /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 +/* Exynos5 Sysreg offset */ +#define EXYNOS5_SYS_I2C_CFG 0x0234 + /* i2c controller state */ enum s3c24xx_i2c_state { STATE_IDLE, @@ -123,6 +128,8 @@ struct s3c24xx_i2c { #if defined(CONFIG_ARM_S3C24XX_CPUFREQ) struct notifier_block freq_transition; #endif + struct regmap *sysreg; + unsigned int sys_i2c_cfg; }; static struct platform_device_id s3c24xx_driver_ids[] = { @@ -1071,6 +1078,7 @@ static void s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { struct s3c2410_platform_i2c *pdata = i2c->pdata; + int id; if (!np) return; @@ -1080,6 +1088,21 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr); of_property_read_u32(np, "samsung,i2c-max-bus-freq", (u32 *)&pdata->frequency); + /* + * Exynos5's legacy i2c controller and new high speed i2c + * controller have muxed interrupt sources. By default the + * interrupts for 4-channel HS-I2C controller are enabled. + * If nodes for first four channels of legacy i2c controller + * are available then re-configure the interrupts via the + * system register. + */ + id = of_alias_get_id(np, "i2c"); + i2c->sysreg = syscon_regmap_lookup_by_phandle(np, + "samsung,sysreg-phandle"); + if (IS_ERR(i2c->sysreg)) + return; + + regmap_update_bits(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, BIT(id), 0); } #else static void @@ -1260,6 +1283,9 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev) i2c->suspended = 1; + if (!IS_ERR(i2c->sysreg)) + regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg); + return 0; } @@ -1268,6 +1294,9 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + if (!IS_ERR(i2c->sysreg)) + regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg); + clk_prepare_enable(i2c->clk); s3c24xx_i2c_init(i2c); clk_disable_unprepare(i2c->clk); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 4855188747c9..421e00418ef1 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -1,6 +1,8 @@ /* * SuperH Mobile I2C Controller * + * Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com> + * * Copyright (C) 2008 Magnus Damm * * Portions of the code based on out-of-tree driver i2c-sh7343.c @@ -16,20 +18,22 @@ * GNU General Public License for more details. */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/i2c/i2c-sh_mobile.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> +#include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/err.h> #include <linux/pm_runtime.h> -#include <linux/clk.h> -#include <linux/io.h> #include <linux/slab.h> -#include <linux/of_device.h> -#include <linux/i2c/i2c-sh_mobile.h> /* Transmit operation: */ /* */ @@ -110,6 +114,7 @@ enum sh_mobile_i2c_op { OP_TX_FIRST, OP_TX, OP_TX_STOP, + OP_TX_STOP_DATA, OP_TX_TO_RX, OP_RX, OP_RX_STOP, @@ -134,6 +139,11 @@ struct sh_mobile_i2c_data { int pos; int sr; bool send_stop; + + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; + struct scatterlist sg; + enum dma_data_direction dma_direction; }; struct sh_mobile_dt_config { @@ -171,6 +181,8 @@ struct sh_mobile_dt_config { #define ICIC_ICCLB8 0x80 #define ICIC_ICCHB8 0x40 +#define ICIC_TDMAE 0x20 +#define ICIC_RDMAE 0x10 #define ICIC_ALE 0x08 #define ICIC_TACKE 0x04 #define ICIC_WAITE 0x02 @@ -277,6 +289,7 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd) else pd->icic &= ~ICIC_ICCHB8; + dev_dbg(pd->dev, "timing values: L/H=0x%x/0x%x\n", pd->iccl, pd->icch); return 0; } @@ -332,8 +345,10 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, case OP_TX: /* write data */ iic_wr(pd, ICDR, data); break; - case OP_TX_STOP: /* write data and issue a stop afterwards */ + case OP_TX_STOP_DATA: /* write data and issue a stop afterwards */ iic_wr(pd, ICDR, data); + /* fallthrough */ + case OP_TX_STOP: /* issue a stop */ iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS : ICCR_ICE | ICCR_TRS | ICCR_BBSY); break; @@ -389,13 +404,17 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { unsigned char data; - if (pd->pos == pd->msg->len) + if (pd->pos == pd->msg->len) { + /* Send stop if we haven't yet (DMA case) */ + if (pd->send_stop && (iic_rd(pd, ICCR) & ICCR_BBSY)) + i2c_op(pd, OP_TX_STOP, 0); return 1; + } sh_mobile_i2c_get_data(pd, &data); if (sh_mobile_i2c_is_last_byte(pd)) - i2c_op(pd, OP_TX_STOP, data); + i2c_op(pd, OP_TX_STOP_DATA, data); else if (sh_mobile_i2c_is_first_byte(pd)) i2c_op(pd, OP_TX_FIRST, data); else @@ -447,10 +466,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) { - struct platform_device *dev = dev_id; - struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); + struct sh_mobile_i2c_data *pd = dev_id; unsigned char sr; - int wakeup; + int wakeup = 0; sr = iic_rd(pd, ICSR); pd->sr |= sr; /* remember state */ @@ -459,15 +477,21 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) (pd->msg->flags & I2C_M_RD) ? "read" : "write", pd->pos, pd->msg->len); - if (sr & (ICSR_AL | ICSR_TACK)) { + /* Kick off TxDMA after preface was done */ + if (pd->dma_direction == DMA_TO_DEVICE && pd->pos == 0) + iic_set_clr(pd, ICIC, ICIC_TDMAE, 0); + else if (sr & (ICSR_AL | ICSR_TACK)) /* don't interrupt transaction - continue to issue stop */ iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK)); - wakeup = 0; - } else if (pd->msg->flags & I2C_M_RD) + else if (pd->msg->flags & I2C_M_RD) wakeup = sh_mobile_i2c_isr_rx(pd); else wakeup = sh_mobile_i2c_isr_tx(pd); + /* Kick off RxDMA after preface was done */ + if (pd->dma_direction == DMA_FROM_DEVICE && pd->pos == 1) + iic_set_clr(pd, ICIC, ICIC_RDMAE, 0); + if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */ iic_wr(pd, ICSR, sr & ~ICSR_WAIT); @@ -482,6 +506,84 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd) +{ + struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE + ? pd->dma_rx : pd->dma_tx; + + dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg), + pd->msg->len, pd->dma_direction); + + pd->dma_direction = DMA_NONE; +} + +static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd) +{ + if (pd->dma_direction == DMA_NONE) + return; + else if (pd->dma_direction == DMA_FROM_DEVICE) + dmaengine_terminate_all(pd->dma_rx); + else if (pd->dma_direction == DMA_TO_DEVICE) + dmaengine_terminate_all(pd->dma_tx); + + sh_mobile_i2c_dma_unmap(pd); +} + +static void sh_mobile_i2c_dma_callback(void *data) +{ + struct sh_mobile_i2c_data *pd = data; + + sh_mobile_i2c_dma_unmap(pd); + pd->pos = pd->msg->len; + + iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); +} + +static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) +{ + bool read = pd->msg->flags & I2C_M_RD; + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct dma_chan *chan = read ? pd->dma_rx : pd->dma_tx; + struct dma_async_tx_descriptor *txdesc; + dma_addr_t dma_addr; + dma_cookie_t cookie; + + if (!chan) + return; + + dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir); + if (dma_mapping_error(pd->dev, dma_addr)) { + dev_dbg(pd->dev, "dma map failed, using PIO\n"); + return; + } + + sg_dma_len(&pd->sg) = pd->msg->len; + sg_dma_address(&pd->sg) = dma_addr; + + pd->dma_direction = dir; + + txdesc = dmaengine_prep_slave_sg(chan, &pd->sg, 1, + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n"); + sh_mobile_i2c_cleanup_dma(pd); + return; + } + + txdesc->callback = sh_mobile_i2c_dma_callback; + txdesc->callback_param = pd; + + cookie = dmaengine_submit(txdesc); + if (dma_submit_error(cookie)) { + dev_dbg(pd->dev, "submitting dma failed, using PIO\n"); + sh_mobile_i2c_cleanup_dma(pd); + return; + } + + dma_async_issue_pending(chan); +} + static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, bool do_init) { @@ -506,6 +608,9 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, pd->pos = -1; pd->sr = 0; + if (pd->msg->len > 8) + sh_mobile_i2c_xfer_dma(pd); + /* Enable all interrupts to begin with */ iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); return 0; @@ -589,6 +694,9 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, 5 * HZ); if (!k) { dev_err(pd->dev, "Transfer request timed out\n"); + if (pd->dma_direction != DMA_NONE) + sh_mobile_i2c_cleanup_dma(pd); + err = -ETIMEDOUT; break; } @@ -622,22 +730,77 @@ static const struct sh_mobile_dt_config default_dt_config = { .clks_per_count = 1, }; -static const struct sh_mobile_dt_config rcar_gen2_dt_config = { +static const struct sh_mobile_dt_config fast_clock_dt_config = { .clks_per_count = 2, }; static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, - { .compatible = "renesas,iic-r8a7790", .data = &rcar_gen2_dt_config }, - { .compatible = "renesas,iic-r8a7791", .data = &rcar_gen2_dt_config }, - { .compatible = "renesas,iic-r8a7792", .data = &rcar_gen2_dt_config }, - { .compatible = "renesas,iic-r8a7793", .data = &rcar_gen2_dt_config }, - { .compatible = "renesas,iic-r8a7794", .data = &rcar_gen2_dt_config }, + { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, {}, }; MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids); -static int sh_mobile_i2c_hook_irqs(struct platform_device *dev) +static int sh_mobile_i2c_request_dma_chan(struct device *dev, enum dma_transfer_direction dir, + dma_addr_t port_addr, struct dma_chan **chan_ptr) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + *chan_ptr = NULL; + + chan = dma_request_slave_channel_reason(dev, chan_name); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); + return ret; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); + dma_release_channel(chan); + return ret; + } + + *chan_ptr = chan; + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return 0; +} + +static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd) +{ + if (pd->dma_tx) { + dma_release_channel(pd->dma_tx); + pd->dma_tx = NULL; + } + + if (pd->dma_rx) { + dma_release_channel(pd->dma_rx); + pd->dma_rx = NULL; + } +} + +static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd) { struct resource *res; resource_size_t n; @@ -646,7 +809,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev) while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { for (n = res->start; n <= res->end; n++) { ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr, - 0, dev_name(&dev->dev), dev); + 0, dev_name(&dev->dev), pd); if (ret) { dev_err(&dev->dev, "cannot request IRQ %pa\n", &n); return ret; @@ -677,7 +840,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) return PTR_ERR(pd->clk); } - ret = sh_mobile_i2c_hook_irqs(dev); + ret = sh_mobile_i2c_hook_irqs(dev, pd); if (ret) return ret; @@ -723,6 +886,21 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) if (ret) return ret; + /* Init DMA */ + sg_init_table(&pd->sg, 1); + pd->dma_direction = DMA_NONE; + ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, + res->start + ICDR, &pd->dma_rx); + if (ret == -EPROBE_DEFER) + return ret; + + ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, + res->start + ICDR, &pd->dma_tx); + if (ret == -EPROBE_DEFER) { + sh_mobile_i2c_release_dma(pd); + return ret; + } + /* Enable Runtime PM for this device. * * Also tell the Runtime PM core to ignore children @@ -754,13 +932,13 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) ret = i2c_add_numbered_adapter(adap); if (ret < 0) { + sh_mobile_i2c_release_dma(pd); dev_err(&dev->dev, "cannot add numbered adapter\n"); return ret; } - dev_info(&dev->dev, - "I2C adapter %d with bus speed %lu Hz (L/H=0x%x/0x%x)\n", - adap->nr, pd->bus_speed, pd->iccl, pd->icch); + dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz, DMA=%c\n", + adap->nr, pd->bus_speed, (pd->dma_rx || pd->dma_tx) ? 'y' : 'n'); return 0; } @@ -770,6 +948,7 @@ static int sh_mobile_i2c_remove(struct platform_device *dev) struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); i2c_del_adapter(&pd->adap); + sh_mobile_i2c_release_dma(pd); pm_runtime_disable(&dev->dev); return 0; } @@ -806,16 +985,15 @@ static int __init sh_mobile_i2c_adap_init(void) { return platform_driver_register(&sh_mobile_i2c_driver); } +subsys_initcall(sh_mobile_i2c_adap_init); static void __exit sh_mobile_i2c_adap_exit(void) { platform_driver_unregister(&sh_mobile_i2c_driver); } - -subsys_initcall(sh_mobile_i2c_adap_init); module_exit(sh_mobile_i2c_adap_exit); MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); -MODULE_AUTHOR("Magnus Damm"); +MODULE_AUTHOR("Magnus Damm and Wolfram Sang"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:i2c-sh_mobile"); diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index cc65ea0b818f..522916a33aa0 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -46,6 +46,11 @@ enum xilinx_i2c_state { STATE_START }; +enum xiic_endian { + LITTLE, + BIG +}; + /** * struct xiic_i2c - Internal representation of the XIIC I2C bus * @base: Memory base of the HW registers @@ -70,6 +75,7 @@ struct xiic_i2c { enum xilinx_i2c_state state; struct i2c_msg *rx_msg; int rx_pos; + enum xiic_endian endianness; }; @@ -170,29 +176,58 @@ struct xiic_i2c { static void xiic_start_xfer(struct xiic_i2c *i2c); static void __xiic_start_xfer(struct xiic_i2c *i2c); +/* + * For the register read and write functions, a little-endian and big-endian + * version are necessary. Endianness is detected during the probe function. + * Only the least significant byte [doublet] of the register are ever + * accessed. This requires an offset of 3 [2] from the base address for + * big-endian systems. + */ + static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) { - iowrite8(value, i2c->base + reg); + if (i2c->endianness == LITTLE) + iowrite8(value, i2c->base + reg); + else + iowrite8(value, i2c->base + reg + 3); } static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) { - return ioread8(i2c->base + reg); + u8 ret; + + if (i2c->endianness == LITTLE) + ret = ioread8(i2c->base + reg); + else + ret = ioread8(i2c->base + reg + 3); + return ret; } static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) { - iowrite16(value, i2c->base + reg); + if (i2c->endianness == LITTLE) + iowrite16(value, i2c->base + reg); + else + iowrite16be(value, i2c->base + reg + 2); } static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) { - iowrite32(value, i2c->base + reg); + if (i2c->endianness == LITTLE) + iowrite32(value, i2c->base + reg); + else + iowrite32be(value, i2c->base + reg); } static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) { - return ioread32(i2c->base + reg); + u32 ret; + + if (i2c->endianness == LITTLE) + ret = ioread32(i2c->base + reg); + else + ret = ioread32be(i2c->base + reg); + return ret; } static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask) @@ -692,6 +727,7 @@ static int xiic_i2c_probe(struct platform_device *pdev) struct resource *res; int ret, irq; u8 i; + u32 sr; i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) @@ -724,6 +760,18 @@ static int xiic_i2c_probe(struct platform_device *pdev) return ret; } + /* + * Detect endianness + * Try to reset the TX FIFO. Then check the EMPTY flag. If it is not + * set, assume that the endianness was wrong and swap. + */ + i2c->endianness = LITTLE; + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + /* Reset is cleared in xiic_reinit */ + sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); + if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK)) + i2c->endianness = BIG; + xiic_reinit(i2c); /* add i2c adapter to i2c tree */ diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 229a89e84b0f..39d25a8cb1ad 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -24,6 +24,7 @@ (c) 2013 Wolfram Sang <wsa@the-dreams.de> I2C ACPI code Copyright (C) 2014 Intel Corp Author: Lan Tianyu <tianyu.lan@intel.com> + I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com> */ #include <linux/module.h> @@ -261,7 +262,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, struct acpi_resource *ares; u32 accessor_type = function >> 16; u8 action = function & ACPI_IO_MASK; - acpi_status ret = AE_OK; + acpi_status ret; int status; ret = acpi_buffer_to_resource(info->connection, info->length, &ares); @@ -628,6 +629,17 @@ static int i2c_device_probe(struct device *dev) if (!client) return 0; + if (!client->irq && dev->of_node) { + int irq = of_irq_get(dev->of_node, 0); + + if (irq == -EPROBE_DEFER) + return irq; + if (irq < 0) + irq = 0; + + client->irq = irq; + } + driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; @@ -1401,7 +1413,6 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, return ERR_PTR(-EINVAL); } - info.irq = irq_of_parse_and_map(node, 0); info.of_node = of_node_get(node); info.archdata = &dev_ad; @@ -1415,7 +1426,6 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, dev_err(&adap->dev, "of_i2c: Failure registering %s\n", node->full_name); of_node_put(node); - irq_dispose_mapping(info.irq); return ERR_PTR(-EINVAL); } return result; @@ -2962,6 +2972,54 @@ trace: } EXPORT_SYMBOL(i2c_smbus_xfer); +int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) +{ + int ret; + + if (!client || !slave_cb) + return -EINVAL; + + if (!(client->flags & I2C_CLIENT_TEN)) { + /* Enforce stricter address checking */ + ret = i2c_check_addr_validity(client->addr); + if (ret) + return ret; + } + + if (!client->adapter->algo->reg_slave) + return -EOPNOTSUPP; + + client->slave_cb = slave_cb; + + i2c_lock_adapter(client->adapter); + ret = client->adapter->algo->reg_slave(client); + i2c_unlock_adapter(client->adapter); + + if (ret) + client->slave_cb = NULL; + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_register); + +int i2c_slave_unregister(struct i2c_client *client) +{ + int ret; + + if (!client->adapter->algo->unreg_slave) + return -EOPNOTSUPP; + + i2c_lock_adapter(client->adapter); + ret = client->adapter->algo->unreg_slave(client); + i2c_unlock_adapter(client->adapter); + + if (ret == 0) + client->slave_cb = NULL; + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_unregister); + MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); MODULE_DESCRIPTION("I2C-Bus main module"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 2d0847b6be62..593f7ca9adc7 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -110,6 +110,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, void *, u32)) { struct i2c_mux_priv *priv; + char symlink_name[20]; int ret; priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); @@ -183,6 +184,12 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, return NULL; } + WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"), + "can't create symlink to mux device\n"); + + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); + WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name), + "can't create symlink for channel %u\n", chan_id); dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); @@ -193,7 +200,12 @@ EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); void i2c_del_mux_adapter(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; + char symlink_name[20]; + + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id); + sysfs_remove_link(&adap->dev.parent->kobj, symlink_name); + sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); i2c_del_adapter(adap); kfree(priv); } diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c new file mode 100644 index 000000000000..6631400b5f02 --- /dev/null +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -0,0 +1,170 @@ +/* + * I2C slave mode EEPROM simulator + * + * Copyright (C) 2014 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> + * Copyright (C) 2014 by Renesas Electronics Corporation + * + * 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; version 2 of the License. + * + * Because most IP blocks can only detect one I2C slave address anyhow, this + * driver does not support simulating EEPROM types which take more than one + * address. It is prepared to simulate bigger EEPROMs with an internal 16 bit + * pointer, yet implementation is deferred until the need actually arises. + */ + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/sysfs.h> + +struct eeprom_data { + struct bin_attribute bin; + bool first_write; + spinlock_t buffer_lock; + u8 buffer_idx; + u8 buffer[]; +}; + +static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct eeprom_data *eeprom = i2c_get_clientdata(client); + + switch (event) { + case I2C_SLAVE_REQ_WRITE_END: + if (eeprom->first_write) { + eeprom->buffer_idx = *val; + eeprom->first_write = false; + } else { + spin_lock(&eeprom->buffer_lock); + eeprom->buffer[eeprom->buffer_idx++] = *val; + spin_unlock(&eeprom->buffer_lock); + } + break; + + case I2C_SLAVE_REQ_READ_START: + spin_lock(&eeprom->buffer_lock); + *val = eeprom->buffer[eeprom->buffer_idx]; + spin_unlock(&eeprom->buffer_lock); + break; + + case I2C_SLAVE_REQ_READ_END: + eeprom->buffer_idx++; + break; + + case I2C_SLAVE_STOP: + eeprom->first_write = true; + break; + + default: + break; + } + + return 0; +} + +static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + struct eeprom_data *eeprom; + unsigned long flags; + + if (off + count >= attr->size) + return -EFBIG; + + eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + spin_lock_irqsave(&eeprom->buffer_lock, flags); + memcpy(buf, &eeprom->buffer[off], count); + spin_unlock_irqrestore(&eeprom->buffer_lock, flags); + + return count; +} + +static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + struct eeprom_data *eeprom; + unsigned long flags; + + if (off + count >= attr->size) + return -EFBIG; + + eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + spin_lock_irqsave(&eeprom->buffer_lock, flags); + memcpy(&eeprom->buffer[off], buf, count); + spin_unlock_irqrestore(&eeprom->buffer_lock, flags); + + return count; +} + +static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct eeprom_data *eeprom; + int ret; + unsigned size = id->driver_data; + + eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + eeprom->first_write = true; + spin_lock_init(&eeprom->buffer_lock); + i2c_set_clientdata(client, eeprom); + + sysfs_bin_attr_init(&eeprom->bin); + eeprom->bin.attr.name = "slave-eeprom"; + eeprom->bin.attr.mode = S_IRUSR | S_IWUSR; + eeprom->bin.read = i2c_slave_eeprom_bin_read; + eeprom->bin.write = i2c_slave_eeprom_bin_write; + eeprom->bin.size = size; + + ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin); + if (ret) + return ret; + + ret = i2c_slave_register(client, i2c_slave_eeprom_slave_cb); + if (ret) { + sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin); + return ret; + } + + return 0; +}; + +static int i2c_slave_eeprom_remove(struct i2c_client *client) +{ + struct eeprom_data *eeprom = i2c_get_clientdata(client); + + i2c_slave_unregister(client); + sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin); + + return 0; +} + +static const struct i2c_device_id i2c_slave_eeprom_id[] = { + { "slave-24c02", 2048 / 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); + +static struct i2c_driver i2c_slave_eeprom_driver = { + .driver = { + .name = "i2c-slave-eeprom", + .owner = THIS_MODULE, + }, + .probe = i2c_slave_eeprom_probe, + .remove = i2c_slave_eeprom_remove, + .id_table = i2c_slave_eeprom_id, +}; +module_i2c_driver(i2c_slave_eeprom_driver); + +MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); +MODULE_DESCRIPTION("I2C slave mode EEPROM simulator"); +MODULE_LICENSE("GPL v2"); |