diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-14 12:54:40 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-14 12:54:40 -0800 |
commit | 96895199c8648db475b38aac5fe6a04ec14a49c4 (patch) | |
tree | 3cb2a2a68a4750de20cf3deddd4a897189164ea9 /drivers/i2c | |
parent | 8fd9589ced9a4ab1cf23296fa1c17d07e883f734 (diff) | |
parent | 6cf710d47633be388e831713738626d1911163c8 (diff) | |
download | linux-stable-96895199c8648db475b38aac5fe6a04ec14a49c4.tar.gz linux-stable-96895199c8648db475b38aac5fe6a04ec14a49c4.tar.bz2 linux-stable-96895199c8648db475b38aac5fe6a04ec14a49c4.zip |
Merge branch 'i2c/for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"For 3.19, the I2C subsystem has to offer special candy this time.
Right in time for Christmas :)
- I2C slave framework: finally, a generic mechanism for Linux being
an I2C slave (if the bus driver supports that). Docs are still
missing but will come later this cycle, the code is good enough to
go.
- I2C muxes represent their topology in sysfs much more detailed.
This will help users to navigate around much easier.
- irq population of i2c clients is now done at probe time, not device
creation time, to have better support for deferred probing.
- new drivers for Imagination SCB, Amlogic Meson
- DMA support added for Freescale IMX, Renesas SHMobile
- slightly bigger driver updates to OMAP, i801, AT91, and rk3x
(mostly quirk handling, timing updates, and using better kernel
interfaces)
- eeprom driver can now write with byte-access (very slow, but OK to
have)
- and the bunch of smaller fixes, cleanups, ID updates..."
* 'i2c/for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (56 commits)
i2c: sh_mobile: remove unneeded DMA mask
i2c: rcar: add slave support
i2c: slave-eeprom: add eeprom simulator driver
i2c: core changes for slave support
MAINTAINERS: add I2C dt bindings also to I2C realm
i2c: designware: Fix falling time bindings doc
i2c: davinci: switch to use platform_get_irq
Documentation: i2c: Use PM ops instead of legacy suspend/resume
i2c: sh_mobile: optimize irq entry
i2c: pxa: add support for SCCB devices
omap: i2c: don't check bus state IP rev3.3 and earlier
i2c: s3c2410: Handle i2c sys_cfg register in i2c driver
i2c: rk3x: add Kconfig dependency on COMMON_CLK
i2c: omap: add notes related to i2c multimaster mode
i2c: omap: don't reset controller if Arbitration Lost detected
i2c: omap: implement workaround for handling invalid BB-bit values
i2c: omap: cleanup register definitions
i2c: rk3x: handle dynamic clock rate changes correctly
i2c: at91: enable probe deferring on dma channel request
i2c: at91: remove legacy DMA support
...
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"); |