diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 16:16:26 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 16:16:26 -0700 |
| commit | acceba598eda9817bc187f3a683a2d2ee7e7fbc7 (patch) | |
| tree | 761b3950789d5eee179b75ecdafafc988ee42a46 /drivers | |
| parent | c19176154b464c861e49355eff636aa6896735b5 (diff) | |
| parent | 5a73882fd2c3a86b502d54da532d373a1f2db15e (diff) | |
| download | linux-acceba598eda9817bc187f3a683a2d2ee7e7fbc7.tar.gz linux-acceba598eda9817bc187f3a683a2d2ee7e7fbc7.tar.bz2 linux-acceba598eda9817bc187f3a683a2d2ee7e7fbc7.zip | |
Merge branch 'i2c/for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"Features:
- new drivers: Renesas EMEV2, register based MUX, NXP LPC2xxx
- core: scans DT and assigns wakeup interrupts. no driver changes needed.
- core: some refcouting issues fixed and better API for that
- core: new helper function for best effort block read emulation
- slave framework: proper DT bindings and userspace instantiation
- some bigger work for xiic, pxa, omap drivers
.. and quite a number of smaller driver fixes, cleanups, improvements"
* 'i2c/for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (65 commits)
i2c: mux: reg Change ioread endianness for readback
i2c: mux: reg: fix compilation warnings
i2c: mux: reg: simplify register size checking
i2c: muxes: fix leaked i2c adapter device node references
i2c: allow specifying separate wakeup interrupt in device tree
of/irq: export of_get_irq_byname()
i2c: xgene-slimpro: dma_mapping_error() doesn't return an error code
i2c: Replace I2C_CROS_EC_TUNNEL dependency
eeprom: at24: use i2c_smbus_read_i2c_block_data_or_emulated
i2c: core: Add support for best effort block read emulation
i2c: lpc2k: add driver
i2c: mux: Add register-based mux i2c-mux-reg
i2c: dt: describe generic bindings
i2c: slave: print warning if slave flag not set
i2c: support 10 bit and slave addresses in sysfs 'new_device'
i2c: take address space into account when checking for used addresses
i2c: apply DT flags when probing
i2c: make address check indpendent from client struct
i2c: rename address check functions
i2c: apply address offset for slaves, too
...
Diffstat (limited to 'drivers')
30 files changed, 1920 insertions, 516 deletions
diff --git a/drivers/clk/shmobile/clk-emev2.c b/drivers/clk/shmobile/clk-emev2.c index 5b60beb7d0eb..a91825471c79 100644 --- a/drivers/clk/shmobile/clk-emev2.c +++ b/drivers/clk/shmobile/clk-emev2.c @@ -28,6 +28,8 @@ #define USIBU1_RSTCTRL 0x0ac #define USIBU2_RSTCTRL 0x0b0 #define USIBU3_RSTCTRL 0x0b4 +#define IIC0_RSTCTRL 0x0dc +#define IIC1_RSTCTRL 0x0e0 #define STI_RSTCTRL 0x124 #define STI_CLKSEL 0x688 @@ -66,6 +68,10 @@ static void __init emev2_smu_init(void) emev2_smu_write(2, USIBU1_RSTCTRL); emev2_smu_write(2, USIBU2_RSTCTRL); emev2_smu_write(2, USIBU3_RSTCTRL); + + /* deassert reset for IIC0->IIC1 */ + emev2_smu_write(1, IIC0_RSTCTRL); + emev2_smu_write(1, IIC1_RSTCTRL); } static void __init emev2_smu_clkdiv_init(struct device_node *np) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 577d58d1f1a1..08b86178e8fb 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -526,6 +526,13 @@ config I2C_EG20T ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. +config I2C_EMEV2 + tristate "EMMA Mobile series I2C adapter" + depends on HAVE_CLK + help + If you say yes to this option, support will be included for the + I2C interface on the Renesas Electronics EM/EV family of processors. + config I2C_EXYNOS5 tristate "Exynos5 high-speed I2C driver" depends on ARCH_EXYNOS && OF @@ -612,6 +619,16 @@ config I2C_KEMPLD This driver can also be built as a module. If so, the module will be called i2c-kempld. +config I2C_LPC2K + tristate "I2C bus support for NXP LPC2K/LPC178x/18xx/43xx" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) + help + This driver supports the I2C interface found several NXP + devices including LPC2xxx, LPC178x/7x and LPC18xx/43xx. + + This driver can also be built as a module. If so, the module + will be called i2c-lpc2k. + config I2C_MESON tristate "Amlogic Meson I2C controller" depends on ARCH_MESON @@ -1123,7 +1140,7 @@ config I2C_SIBYTE config I2C_CROS_EC_TUNNEL tristate "ChromeOS EC tunnel I2C bus" - depends on CROS_EC_PROTO + depends on MFD_CROS_EC help If you say yes here you get an I2C bus that will tunnel i2c commands through to the other side of the ChromeOS EC to the i2c bus diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e5f537c80da0..6df3b303bd09 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -48,6 +48,7 @@ i2c-designware-pci-objs := i2c-designware-pcidrv.o obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o +obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o @@ -58,6 +59,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o +obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 2ee78e099d30..84deed6571bd 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -17,6 +17,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/of.h> /* Register offsets for the I2C device. */ #define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */ @@ -113,6 +114,8 @@ #define CDNS_I2C_TIMEOUT_MAX 0xFF +#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0) + #define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset) #define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset) @@ -135,6 +138,7 @@ * @bus_hold_flag: Flag used in repeated start for clearing HOLD bit * @clk: Pointer to struct clk * @clk_rate_change_nb: Notifier block for clock rate changes + * @quirks: flag for broken hold bit usage in r1p10 */ struct cdns_i2c { void __iomem *membase; @@ -154,6 +158,11 @@ struct cdns_i2c { unsigned int bus_hold_flag; struct clk *clk; struct notifier_block clk_rate_change_nb; + u32 quirks; +}; + +struct cdns_platform_data { + u32 quirks; }; #define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \ @@ -172,6 +181,12 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id) cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET); } +static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround) +{ + return (hold_wrkaround && + (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)); +} + /** * cdns_i2c_isr - Interrupt handler for the I2C device * @irq: irq number for the I2C device @@ -186,6 +201,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) { unsigned int isr_status, avail_bytes, updatetx; unsigned int bytes_to_send; + bool hold_quirk; struct cdns_i2c *id = ptr; /* Signal completion only after everything is updated */ int done_flag = 0; @@ -208,6 +224,8 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) if (id->recv_count > id->curr_recv_count) updatetx = 1; + hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx; + /* When receiving, handle data interrupt and completion interrupt */ if (id->p_recv_buf && ((isr_status & CDNS_I2C_IXR_COMP) || @@ -229,8 +247,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) id->recv_count--; id->curr_recv_count--; - if (updatetx && - (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)) + if (cdns_is_holdquirk(id, hold_quirk)) break; } @@ -241,8 +258,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) * maintain transfer size non-zero while performing a large * receive operation. */ - if (updatetx && - (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)) { + if (cdns_is_holdquirk(id, hold_quirk)) { /* wait while fifo is full */ while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) != (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH)) @@ -264,6 +280,22 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr) CDNS_I2C_XFER_SIZE_OFFSET); id->curr_recv_count = id->recv_count; } + } else if (id->recv_count && !hold_quirk && + !id->curr_recv_count) { + + /* Set the slave address in address register*/ + cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, + CDNS_I2C_ADDR_OFFSET); + + if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) { + cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE, + CDNS_I2C_XFER_SIZE_OFFSET); + id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE; + } else { + cdns_i2c_writereg(id->recv_count, + CDNS_I2C_XFER_SIZE_OFFSET); + id->curr_recv_count = id->recv_count; + } } /* Clear hold (if not repeated start) and signal completion */ @@ -535,11 +567,13 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int ret, count; u32 reg; struct cdns_i2c *id = adap->algo_data; + bool hold_quirk; /* Check if the bus is free */ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) return -EAGAIN; + hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT); /* * Set the flag to one when multiple messages are to be * processed with a repeated start. @@ -552,7 +586,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, * followed by any other message, an error is returned * indicating that this sequence is not supported. */ - for (count = 0; count < num - 1; count++) { + for (count = 0; (count < num - 1 && hold_quirk); count++) { if (msgs[count].flags & I2C_M_RD) { dev_warn(adap->dev.parent, "Can't do repeated start after a receive message\n"); @@ -815,6 +849,17 @@ static int __maybe_unused cdns_i2c_resume(struct device *_dev) static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend, cdns_i2c_resume); +static const struct cdns_platform_data r1p10_i2c_def = { + .quirks = CDNS_I2C_BROKEN_HOLD_BIT, +}; + +static const struct of_device_id cdns_i2c_of_match[] = { + { .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def }, + { .compatible = "cdns,i2c-r1p14",}, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, cdns_i2c_of_match); + /** * cdns_i2c_probe - Platform registration call * @pdev: Handle to the platform device structure @@ -830,6 +875,7 @@ static int cdns_i2c_probe(struct platform_device *pdev) struct resource *r_mem; struct cdns_i2c *id; int ret; + const struct of_device_id *match; id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL); if (!id) @@ -837,6 +883,12 @@ static int cdns_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, id); + match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node); + if (match && match->data) { + const struct cdns_platform_data *data = match->data; + id->quirks = data->quirks; + } + r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); id->membase = devm_ioremap_resource(&pdev->dev, r_mem); if (IS_ERR(id->membase)) @@ -844,6 +896,7 @@ static int cdns_i2c_probe(struct platform_device *pdev) id->irq = platform_get_irq(pdev, 0); + id->adap.owner = THIS_MODULE; id->adap.dev.of_node = pdev->dev.of_node; id->adap.algo = &cdns_i2c_algo; id->adap.timeout = CDNS_I2C_TIMEOUT; @@ -935,12 +988,6 @@ static int cdns_i2c_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id cdns_i2c_of_match[] = { - { .compatible = "cdns,i2c-r1p10", }, - { /* end of table */ } -}; -MODULE_DEVICE_TABLE(of, cdns_i2c_of_match); - static struct platform_driver cdns_i2c_drv = { .driver = { .name = DRIVER_NAME, diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 6f19a33773fe..7441cdc1b34a 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -777,8 +777,7 @@ irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) enabled = dw_readl(dev, DW_IC_ENABLE); stat = dw_readl(dev, DW_IC_RAW_INTR_STAT); - dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__, - dev->adapter.name, enabled, stat); + dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat); if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) return IRQ_NONE; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 6643d2dc0b25..df23e8c30e6f 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -260,8 +260,8 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci"); - r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, IRQF_SHARED, - adap->name, dev); + r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, + IRQF_SHARED | IRQF_COND_SUSPEND, adap->name, dev); if (r) { dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); return r; diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c new file mode 100644 index 000000000000..192ef6b50c79 --- /dev/null +++ b/drivers/i2c/busses/i2c-emev2.c @@ -0,0 +1,332 @@ +/* + * I2C driver for the Renesas EMEV2 SoC + * + * Copyright (C) 2015 Wolfram Sang <wsa@sang-engineering.com> + * Copyright 2013 Codethink Ltd. + * Copyright 2010-2015 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 version 2 + * as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/device.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_device.h> +#include <linux/platform_device.h> +#include <linux/sched.h> + +/* I2C Registers */ +#define I2C_OFS_IICACT0 0x00 /* start */ +#define I2C_OFS_IIC0 0x04 /* shift */ +#define I2C_OFS_IICC0 0x08 /* control */ +#define I2C_OFS_SVA0 0x0c /* slave address */ +#define I2C_OFS_IICCL0 0x10 /* clock select */ +#define I2C_OFS_IICX0 0x14 /* extension */ +#define I2C_OFS_IICS0 0x18 /* status */ +#define I2C_OFS_IICSE0 0x1c /* status For emulation */ +#define I2C_OFS_IICF0 0x20 /* IIC flag */ + +/* I2C IICACT0 Masks */ +#define I2C_BIT_IICE0 0x0001 + +/* I2C IICC0 Masks */ +#define I2C_BIT_LREL0 0x0040 +#define I2C_BIT_WREL0 0x0020 +#define I2C_BIT_SPIE0 0x0010 +#define I2C_BIT_WTIM0 0x0008 +#define I2C_BIT_ACKE0 0x0004 +#define I2C_BIT_STT0 0x0002 +#define I2C_BIT_SPT0 0x0001 + +/* I2C IICCL0 Masks */ +#define I2C_BIT_SMC0 0x0008 +#define I2C_BIT_DFC0 0x0004 + +/* I2C IICSE0 Masks */ +#define I2C_BIT_MSTS0 0x0080 +#define I2C_BIT_ALD0 0x0040 +#define I2C_BIT_EXC0 0x0020 +#define I2C_BIT_COI0 0x0010 +#define I2C_BIT_TRC0 0x0008 +#define I2C_BIT_ACKD0 0x0004 +#define I2C_BIT_STD0 0x0002 +#define I2C_BIT_SPD0 0x0001 + +/* I2C IICF0 Masks */ +#define I2C_BIT_STCF 0x0080 +#define I2C_BIT_IICBSY 0x0040 +#define I2C_BIT_STCEN 0x0002 +#define I2C_BIT_IICRSV 0x0001 + +struct em_i2c_device { + void __iomem *base; + struct i2c_adapter adap; + struct completion msg_done; + struct clk *sclk; +}; + +static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg) +{ + writeb((readb(priv->base + reg) & ~clear) | set, priv->base + reg); +} + +static int em_i2c_wait_for_event(struct em_i2c_device *priv) +{ + unsigned long time_left; + int status; + + reinit_completion(&priv->msg_done); + + time_left = wait_for_completion_timeout(&priv->msg_done, priv->adap.timeout); + + if (!time_left) + return -ETIMEDOUT; + + status = readb(priv->base + I2C_OFS_IICSE0); + return status & I2C_BIT_ALD0 ? -EAGAIN : status; +} + +static void em_i2c_stop(struct em_i2c_device *priv) +{ + /* Send Stop condition */ + em_clear_set_bit(priv, 0, I2C_BIT_SPT0 | I2C_BIT_SPIE0, I2C_OFS_IICC0); + + /* Wait for stop condition */ + em_i2c_wait_for_event(priv); +} + +static void em_i2c_reset(struct i2c_adapter *adap) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int retr; + + /* If I2C active */ + if (readb(priv->base + I2C_OFS_IICACT0) & I2C_BIT_IICE0) { + /* Disable I2C operation */ + writeb(0, priv->base + I2C_OFS_IICACT0); + + retr = 1000; + while (readb(priv->base + I2C_OFS_IICACT0) == 1 && retr) + retr--; + WARN_ON(retr == 0); + } + + /* Transfer mode set */ + writeb(I2C_BIT_DFC0, priv->base + I2C_OFS_IICCL0); + + /* Can Issue start without detecting a stop, Reservation disabled. */ + writeb(I2C_BIT_STCEN | I2C_BIT_IICRSV, priv->base + I2C_OFS_IICF0); + + /* I2C enable, 9 bit interrupt mode */ + writeb(I2C_BIT_WTIM0, priv->base + I2C_OFS_IICC0); + + /* Enable I2C operation */ + writeb(I2C_BIT_IICE0, priv->base + I2C_OFS_IICACT0); + + retr = 1000; + while (readb(priv->base + I2C_OFS_IICACT0) == 0 && retr) + retr--; + WARN_ON(retr == 0); +} + +static int __em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int stop) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int count, status, read = !!(msg->flags & I2C_M_RD); + + /* Send start condition */ + em_clear_set_bit(priv, 0, I2C_BIT_ACKE0 | I2C_BIT_WTIM0, I2C_OFS_IICC0); + em_clear_set_bit(priv, 0, I2C_BIT_STT0, I2C_OFS_IICC0); + + /* Send slave address and R/W type */ + writeb((msg->addr << 1) | read, priv->base + I2C_OFS_IIC0); + + /* Wait for transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + + /* Received NACK (result of setting slave address and R/W) */ + if (!(status & I2C_BIT_ACKD0)) { + em_i2c_stop(priv); + goto out; + } + + /* Extra setup for read transactions */ + if (read) { + /* 8 bit interrupt mode */ + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0, I2C_OFS_IICC0); + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0, I2C_OFS_IICC0); + + /* Wait for transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + } + + /* Send / receive data */ + for (count = 0; count < msg->len; count++) { + if (read) { /* Read transaction */ + msg->buf[count] = readb(priv->base + I2C_OFS_IIC0); + em_clear_set_bit(priv, 0, I2C_BIT_WREL0, I2C_OFS_IICC0); + + } else { /* Write transaction */ + /* Received NACK */ + if (!(status & I2C_BIT_ACKD0)) { + em_i2c_stop(priv); + goto out; + } + + /* Write data */ + writeb(msg->buf[count], priv->base + I2C_OFS_IIC0); + } + + /* Wait for R/W transaction */ + status = em_i2c_wait_for_event(priv); + if (status < 0) + goto out_reset; + } + + if (stop) + em_i2c_stop(priv); + + return count; + +out_reset: + em_i2c_reset(adap); +out: + return status < 0 ? status : -ENXIO; +} + +static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct em_i2c_device *priv = i2c_get_adapdata(adap); + int ret, i; + + if (readb(priv->base + I2C_OFS_IICF0) & I2C_BIT_IICBSY) + return -EAGAIN; + + for (i = 0; i < num; i++) { + ret = __em_i2c_xfer(adap, &msgs[i], (i == (num - 1))); + if (ret < 0) + return ret; + } + + /* I2C transfer completed */ + return num; +} + +static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id) +{ + struct em_i2c_device *priv = dev_id; + + complete(&priv->msg_done); + return IRQ_HANDLED; +} + +static u32 em_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm em_i2c_algo = { + .master_xfer = em_i2c_xfer, + .functionality = em_i2c_func, +}; + +static int em_i2c_probe(struct platform_device *pdev) +{ + struct em_i2c_device *priv; + struct resource *r; + int irq, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + strlcpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name)); + + priv->sclk = devm_clk_get(&pdev->dev, "sclk"); + if (IS_ERR(priv->sclk)) + return PTR_ERR(priv->sclk); + + clk_prepare_enable(priv->sclk); + + priv->adap.timeout = msecs_to_jiffies(100); + priv->adap.retries = 5; + priv->adap.dev.parent = &pdev->dev; + priv->adap.algo = &em_i2c_algo; + priv->adap.owner = THIS_MODULE; + priv->adap.dev.of_node = pdev->dev.of_node; + + init_completion(&priv->msg_done); + + platform_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adap, priv); + + em_i2c_reset(&priv->adap); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, em_i2c_irq_handler, 0, + "em_i2c", priv); + if (ret) + goto err_clk; + + ret = i2c_add_adapter(&priv->adap); + + if (ret) + goto err_clk; + + dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, irq); + + return 0; + +err_clk: + clk_disable_unprepare(priv->sclk); + return ret; +} + +static int em_i2c_remove(struct platform_device *dev) +{ + struct em_i2c_device *priv = platform_get_drvdata(dev); + + i2c_del_adapter(&priv->adap); + clk_disable_unprepare(priv->sclk); + + return 0; +} + +static const struct of_device_id em_i2c_ids[] = { + { .compatible = "renesas,iic-emev2", }, + { } +}; + +static struct platform_driver em_i2c_driver = { + .probe = em_i2c_probe, + .remove = em_i2c_remove, + .driver = { + .name = "em-i2c", + .of_match_table = em_i2c_ids, + } +}; +module_platform_driver(em_i2c_driver); + +MODULE_DESCRIPTION("EMEV2 I2C bus driver"); +MODULE_AUTHOR("Ian Molton and Wolfram Sang <wsa@sang-engineering.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, em_i2c_ids); diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c new file mode 100644 index 000000000000..8560a13bf1b3 --- /dev/null +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2011 NXP Semiconductors + * + * Code portions referenced from the i2x-pxa and i2c-pnx drivers + * + * Make SMBus byte and word transactions work on LPC178x/7x + * Copyright (c) 2012 + * Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com + * Anton Protopopov, Emcraft Systems, antonp@emcraft.com + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/clk.h> +#include <linux/errno.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/of_device.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/time.h> + +/* LPC24xx register offsets and bits */ +#define LPC24XX_I2CONSET 0x00 +#define LPC24XX_I2STAT 0x04 +#define LPC24XX_I2DAT 0x08 +#define LPC24XX_I2ADDR 0x0c +#define LPC24XX_I2SCLH 0x10 +#define LPC24XX_I2SCLL 0x14 +#define LPC24XX_I2CONCLR 0x18 + +#define LPC24XX_AA BIT(2) +#define LPC24XX_SI BIT(3) +#define LPC24XX_STO BIT(4) +#define LPC24XX_STA BIT(5) +#define LPC24XX_I2EN BIT(6) + +#define LPC24XX_STO_AA (LPC24XX_STO | LPC24XX_AA) +#define LPC24XX_CLEAR_ALL (LPC24XX_AA | LPC24XX_SI | LPC24XX_STO | \ + LPC24XX_STA | LPC24XX_I2EN) + +/* I2C SCL clock has different duty cycle depending on mode */ +#define I2C_STD_MODE_DUTY 46 +#define I2C_FAST_MODE_DUTY 36 +#define I2C_FAST_MODE_PLUS_DUTY 38 + +/* + * 26 possible I2C status codes, but codes applicable only + * to master are listed here and used in this driver + */ +enum { + M_BUS_ERROR = 0x00, + M_START = 0x08, + M_REPSTART = 0x10, + MX_ADDR_W_ACK = 0x18, + MX_ADDR_W_NACK = 0x20, + MX_DATA_W_ACK = 0x28, + MX_DATA_W_NACK = 0x30, + M_DATA_ARB_LOST = 0x38, + MR_ADDR_R_ACK = 0x40, + MR_ADDR_R_NACK = 0x48, + MR_DATA_R_ACK = 0x50, + MR_DATA_R_NACK = 0x58, + M_I2C_IDLE = 0xf8, +}; + +struct lpc2k_i2c { + void __iomem *base; + struct clk *clk; + int irq; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int msg_idx; + int msg_status; + int is_last; +}; + +static void i2c_lpc2k_reset(struct lpc2k_i2c *i2c) +{ + /* Will force clear all statuses */ + writel(LPC24XX_CLEAR_ALL, i2c->base + LPC24XX_I2CONCLR); + writel(0, i2c->base + LPC24XX_I2ADDR); + writel(LPC24XX_I2EN, i2c->base + LPC24XX_I2CONSET); +} + +static int i2c_lpc2k_clear_arb(struct lpc2k_i2c *i2c) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + /* + * If the transfer needs to abort for some reason, we'll try to + * force a stop condition to clear any pending bus conditions + */ + writel(LPC24XX_STO, i2c->base + LPC24XX_I2CONSET); + + /* Wait for status change */ + while (readl(i2c->base + LPC24XX_I2STAT) != M_I2C_IDLE) { + if (time_after(jiffies, timeout)) { + /* Bus was not idle, try to reset adapter */ + i2c_lpc2k_reset(i2c); + return -EBUSY; + } + + cpu_relax(); + } + + return 0; +} + +static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c) +{ + unsigned char data; + u32 status; + + /* + * I2C in the LPC2xxx series is basically a state machine. + * Just run through the steps based on the current status. + */ + status = readl(i2c->base + LPC24XX_I2STAT); + + switch (status) { + case M_START: + case M_REPSTART: + /* Start bit was just sent out, send out addr and dir */ + data = i2c->msg->addr << 1; + if (i2c->msg->flags & I2C_M_RD) + data |= 1; |
