diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-09 08:45:40 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-09 08:45:40 -0700 |
commit | 39de65aa2c3eee901db020a4f1396998e09602a3 (patch) | |
tree | 0582450ec0b5b6b9778275b431207145d5fedfc8 /drivers/i2c | |
parent | 97e18dc007546fce8e99098480b921a02ebb3037 (diff) | |
parent | 1fbeab0b8fd5e655ffef8a793b869eb7dffe0337 (diff) |
Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"Here is the pull request from the i2c subsystem. It got a little
delayed because I needed to wait for a dependency to be included
(commit b424080a9e08: "reset: Add optional resets and stubs"). Plus,
I had some email problems. All done now, the highlights are:
- drivers can now deprecate their use of i2c classes. That shouldn't
be used on embedded platforms anyhow and was often blindly
copy&pasted. This mechanism gives users time to switch away and
ultimately boot faster once the use of classes for those drivers is
gone for good.
- new drivers for QUP, Cadence, efm32
- tracepoint support for I2C and SMBus
- bigger cleanups for the mv64xxx, nomadik, and designware drivers
And the usual bugfixes, cleanups, feature additions. Most stuff has
been in linux-next for a while. Just some hot fixes and new drivers
were added a bit more recently."
* 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (63 commits)
i2c: cadence: fix Kconfig dependency
i2c: Add driver for Cadence I2C controller
i2c: cadence: Document device tree bindings
Documentation: i2c: improve section about flags mangling the protocol
i2c: qup: use proper type fro clk_freq
i2c: qup: off by ones in qup_i2c_probe()
i2c: efm32: fix binding doc
MAINTAINERS: update I2C web resources
i2c: qup: New bus driver for the Qualcomm QUP I2C controller
i2c: qup: Add device tree bindings information
i2c: i2c-xiic: deprecate class based instantiation
i2c: i2c-sirf: deprecate class based instantiation
i2c: i2c-mv64xxx: deprecate class based instantiation
i2c: i2c-designware-platdrv: deprecate class based instantiation
i2c: i2c-davinci: deprecate class based instantiation
i2c: i2c-bcm2835: deprecate class based instantiation
i2c: mv64xxx: Fix reset controller handling
i2c: omap: fix usage of IS_ERR_VALUE with pm_runtime_get_sync
i2c: efm32: new bus driver
i2c: exynos5: remove unnecessary cast of void pointer
...
Diffstat (limited to 'drivers/i2c')
47 files changed, 2593 insertions, 345 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 014afab1d55..c94db1c5e35 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -110,6 +110,7 @@ config I2C_I801 Wellsburg (PCH) Coleto Creek (PCH) Wildcat Point-LP (PCH) + BayTrail (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -375,6 +376,13 @@ config I2C_BLACKFIN_TWI_CLK_KHZ help The unit of the TWI clock is kHz. +config I2C_CADENCE + tristate "Cadence I2C Controller" + depends on ARCH_ZYNQ + help + Say yes here to select Cadence I2C Host Controller. This controller is + e.g. used by Xilinx Zynq. + config I2C_CBUS_GPIO tristate "CBUS I2C driver" depends on GPIOLIB @@ -432,6 +440,13 @@ config I2C_DESIGNWARE_PCI This driver can also be built as a module. If so, the module will be called i2c-designware-pci. +config I2C_EFM32 + tristate "EFM32 I2C controller" + depends on ARCH_EFM32 || COMPILE_TEST + help + This driver supports the i2c block found in Energy Micro's EFM32 + SoCs. + config I2C_EG20T tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" depends on PCI @@ -527,7 +542,7 @@ config I2C_MPC config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on (MV64X60 || PLAT_ORION || ARCH_SUNXI) + depends on MV64X60 || PLAT_ORION || ARCH_SUNXI help If you say yes to this option, support will be included for the built-in I2C interface on the Marvell 64xxx line of host bridges. @@ -648,6 +663,16 @@ config I2C_PXA_SLAVE is necessary for systems where the PXA may be a target on the I2C bus. +config I2C_QUP + tristate "Qualcomm QUP based I2C controller" + depends on ARCH_QCOM + help + If you say yes to this option, support will be included for the + built-in I2C interface on the Qualcomm SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-qup. + config I2C_RIIC tristate "Renesas RIIC adapter" depends on ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index a08931fe73e..18d18ff9db9 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o @@ -41,6 +42,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o i2c-designware-platform-objs := i2c-designware-platdrv.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-objs := i2c-designware-pcidrv.o +obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o @@ -63,6 +65,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o +obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_RIIC) += i2c-riic.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o obj-$(CONFIG_I2C_S6000) += i2c-s6000.o diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index 7d60d3a1f62..451e305f797 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -494,7 +494,7 @@ static struct i2c_adapter ali1535_adapter = { .algo = &smbus_algorithm, }; -static DEFINE_PCI_DEVICE_TABLE(ali1535_ids) = { +static const struct pci_device_id ali1535_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, { }, }; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index 4611e4754a6..98a1c97739b 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -416,7 +416,7 @@ static void ali1563_remove(struct pci_dev *dev) ali1563_shutdown(dev); } -static DEFINE_PCI_DEVICE_TABLE(ali1563_id_table) = { +static const struct pci_device_id ali1563_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) }, {}, }; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 4823206a487..2fa21ce9682 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -476,7 +476,7 @@ static struct i2c_adapter ali15x3_adapter = { .algo = &smbus_algorithm, }; -static DEFINE_PCI_DEVICE_TABLE(ali15x3_ids) = { +static const struct pci_device_id ali15x3_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 819d3c1062a..a16f7289135 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -307,7 +307,7 @@ static const char* chipname[] = { "nVidia nForce", "AMD8111", }; -static DEFINE_PCI_DEVICE_TABLE(amd756_ids) = { +static const struct pci_device_id amd756_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B), .driver_data = AMD756 }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413), diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index f3d4d79855b..95a80a8f81b 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -414,7 +414,7 @@ static const struct i2c_algorithm smbus_algorithm = { }; -static DEFINE_PCI_DEVICE_TABLE(amd8111_ids) = { +static const struct pci_device_id amd8111_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 843d01268ae..e95f9ba9679 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -32,7 +32,7 @@ #include <linux/slab.h> #include <linux/platform_data/dma-atmel.h> -#define TWI_CLK_HZ 100000 /* max 400 Kbits/s */ +#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 */ @@ -711,6 +711,7 @@ static int at91_twi_probe(struct platform_device *pdev) struct resource *mem; int rc; u32 phy_addr; + u32 bus_clk_rate; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -756,13 +757,18 @@ static int at91_twi_probe(struct platform_device *pdev) dev->use_dma = true; } - at91_calc_twi_clock(dev, TWI_CLK_HZ); + rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", + &bus_clk_rate); + if (rc) + bus_clk_rate = DEFAULT_TWI_CLK_HZ; + + at91_calc_twi_clock(dev, bus_clk_rate); at91_init_twi_bus(dev); snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91"); i2c_set_adapdata(&dev->adapter, dev); dev->adapter.owner = THIS_MODULE; - dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED; dev->adapter.algo = &at91_twi_algorithm; dev->adapter.dev.parent = dev->dev; dev->adapter.nr = pdev->id; diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 77df97b932a..c60719577fc 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -219,7 +219,7 @@ static const struct i2c_algorithm bcm2835_i2c_algo = { static int bcm2835_i2c_probe(struct platform_device *pdev) { struct bcm2835_i2c_dev *i2c_dev; - struct resource *mem, *requested, *irq; + struct resource *mem, *irq; u32 bus_clk_rate, divider; int ret; struct i2c_adapter *adap; @@ -234,25 +234,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) init_completion(&i2c_dev->completion); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No mem resource\n"); - return -ENODEV; - } - - requested = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), - dev_name(&pdev->dev)); - if (!requested) { - dev_err(&pdev->dev, "Could not claim register region\n"); - return -EBUSY; - } - - i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start, - resource_size(mem)); - if (!i2c_dev->regs) { - dev_err(&pdev->dev, "Could not map registers\n"); - return -ENOMEM; - } + i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(i2c_dev->regs)) + return PTR_ERR(i2c_dev->regs); i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_dev->clk)) { @@ -295,7 +279,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) adap = &i2c_dev->adapter; i2c_set_adapdata(adap, i2c_dev); adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_HWMON; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED; strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name)); adap->algo = &bcm2835_i2c_algo; adap->dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 3b9bd9a3f2b..e6d5162b637 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -21,10 +21,10 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/i2c/bfin_twi.h> -#include <asm/blackfin.h> -#include <asm/portmux.h> #include <asm/irq.h> +#include <asm/portmux.h> #include <asm/bfin_twi.h> /* SMBus mode*/ @@ -65,7 +65,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, /* Transmit next data */ while (iface->writeNum > 0 && (read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) { - SSYNC(); write_XMT_DATA8(iface, *(iface->transPtr++)); iface->writeNum--; } @@ -248,7 +247,6 @@ static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id) /* Clear interrupt status */ write_INT_STAT(iface, twi_int_status); bfin_twi_handle_interrupt(iface, twi_int_status); - SSYNC(); } spin_unlock_irqrestore(&iface->lock, flags); return IRQ_HANDLED; @@ -294,9 +292,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, * discarded before start a new operation. */ write_FIFO_CTL(iface, 0x3); - SSYNC(); write_FIFO_CTL(iface, 0); - SSYNC(); if (pmsg->flags & I2C_M_RD) iface->read_write = I2C_SMBUS_READ; @@ -306,7 +302,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, if (iface->writeNum > 0) { write_XMT_DATA8(iface, *(iface->transPtr++)); iface->writeNum--; - SSYNC(); } } @@ -315,7 +310,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, /* Interrupt mask . Enable XMT, RCV interrupt */ write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); - SSYNC(); if (pmsg->len <= 255) write_MASTER_CTL(iface, pmsg->len << 6); @@ -329,7 +323,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, (iface->msg_num > 1 ? RSTART : 0) | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); - SSYNC(); while (!iface->result) { if (!wait_for_completion_timeout(&iface->complete, @@ -453,7 +446,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, * start a new operation. */ write_FIFO_CTL(iface, 0x3); - SSYNC(); write_FIFO_CTL(iface, 0); /* clear int stat */ @@ -461,7 +453,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, /* Set Transmit device address */ write_MASTER_ADDR(iface, addr); - SSYNC(); switch (iface->cur_mode) { case TWI_I2C_MODE_STANDARDSUB: @@ -469,7 +460,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, write_INT_MASK(iface, MCOMP | MERR | ((iface->read_write == I2C_SMBUS_READ) ? RCVSERV : XMTSERV)); - SSYNC(); if (iface->writeNum + 1 <= 255) write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); @@ -484,7 +474,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, case TWI_I2C_MODE_COMBINED: write_XMT_DATA8(iface, iface->command); write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); - SSYNC(); if (iface->writeNum > 0) write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); @@ -531,7 +520,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, write_INT_MASK(iface, MCOMP | MERR | ((iface->read_write == I2C_SMBUS_READ) ? RCVSERV : XMTSERV)); - SSYNC(); /* Master enable */ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | @@ -539,7 +527,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); break; } - SSYNC(); while (!iface->result) { if (!wait_for_completion_timeout(&iface->complete, @@ -669,7 +656,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); p_adap->algo = &bfin_twi_algorithm; p_adap->algo_data = iface; - p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED; p_adap->dev.parent = &pdev->dev; p_adap->timeout = 5 * HZ; p_adap->retries = 3; @@ -704,7 +691,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) /* Enable TWI */ write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); - SSYNC(); rc = i2c_add_numbered_adapter(p_adap); if (rc < 0) { diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c new file mode 100644 index 00000000000..63f3f03ecc9 --- /dev/null +++ b/drivers/i2c/busses/i2c-cadence.c @@ -0,0 +1,905 @@ +/* + * I2C bus driver for the Cadence I2C controller. + * + * Copyright (C) 2009 - 2014 Xilinx, Inc. + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +/* Register offsets for the I2C device. */ +#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */ +#define CDNS_I2C_SR_OFFSET 0x04 /* Status Register, RO */ +#define CDNS_I2C_ADDR_OFFSET 0x08 /* I2C Address Register, RW */ +#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */ +#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */ +#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */ +#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */ +#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */ +#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */ + +/* Control Register Bit mask definitions */ +#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */ +#define CDNS_I2C_CR_ACK_EN BIT(3) +#define CDNS_I2C_CR_NEA BIT(2) +#define CDNS_I2C_CR_MS BIT(1) +/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */ +#define CDNS_I2C_CR_RW BIT(0) +/* 1 = Auto init FIFO to zeroes */ +#define CDNS_I2C_CR_CLR_FIFO BIT(6) +#define CDNS_I2C_CR_DIVA_SHIFT 14 +#define CDNS_I2C_CR_DIVA_MASK (3 << CDNS_I2C_CR_DIVA_SHIFT) +#define CDNS_I2C_CR_DIVB_SHIFT 8 +#define CDNS_I2C_CR_DIVB_MASK (0x3f << CDNS_I2C_CR_DIVB_SHIFT) + +/* Status Register Bit mask definitions */ +#define CDNS_I2C_SR_BA BIT(8) +#define CDNS_I2C_SR_RXDV BIT(5) + +/* + * I2C Address Register Bit mask definitions + * Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0] + * bits. A write access to this register always initiates a transfer if the I2C + * is in master mode. + */ +#define CDNS_I2C_ADDR_MASK 0x000003FF /* I2C Address Mask */ + +/* + * I2C Interrupt Registers Bit mask definitions + * All the four interrupt registers (Status/Mask/Enable/Disable) have the same + * bit definitions. + */ +#define CDNS_I2C_IXR_ARB_LOST BIT(9) +#define CDNS_I2C_IXR_RX_UNF BIT(7) +#define CDNS_I2C_IXR_TX_OVF BIT(6) +#define CDNS_I2C_IXR_RX_OVF BIT(5) +#define CDNS_I2C_IXR_SLV_RDY BIT(4) +#define CDNS_I2C_IXR_TO BIT(3) +#define CDNS_I2C_IXR_NACK BIT(2) +#define CDNS_I2C_IXR_DATA BIT(1) +#define CDNS_I2C_IXR_COMP BIT(0) + +#define CDNS_I2C_IXR_ALL_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_SLV_RDY | \ + CDNS_I2C_IXR_TO | \ + CDNS_I2C_IXR_NACK | \ + CDNS_I2C_IXR_DATA | \ + CDNS_I2C_IXR_COMP) + +#define CDNS_I2C_IXR_ERR_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_NACK) + +#define CDNS_I2C_ENABLED_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \ + CDNS_I2C_IXR_RX_UNF | \ + CDNS_I2C_IXR_TX_OVF | \ + CDNS_I2C_IXR_RX_OVF | \ + CDNS_I2C_IXR_NACK | \ + CDNS_I2C_IXR_DATA | \ + CDNS_I2C_IXR_COMP) + +#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000) + +#define CDNS_I2C_FIFO_DEPTH 16 +/* FIFO depth at which the DATA interrupt occurs */ +#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2) +#define CDNS_I2C_MAX_TRANSFER_SIZE 255 +/* Transfer size in multiples of data interrupt depth */ +#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3) + +#define DRIVER_NAME "cdns-i2c" + +#define CDNS_I2C_SPEED_MAX 400000 +#define CDNS_I2C_SPEED_DEFAULT 100000 + +#define CDNS_I2C_DIVA_MAX 4 +#define CDNS_I2C_DIVB_MAX 64 + +#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset) +#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset) + +/** + * struct cdns_i2c - I2C device private data structure + * @membase: Base address of the I2C device + * @adap: I2C adapter instance + * @p_msg: Message pointer + * @err_status: Error status in Interrupt Status Register + * @xfer_done: Transfer complete status + * @p_send_buf: Pointer to transmit buffer + * @p_recv_buf: Pointer to receive buffer + * @suspended: Flag holding the device's PM status + * @send_count: Number of bytes still expected to send + * @recv_count: Number of bytes still expected to receive + * @irq: IRQ number + * @input_clk: Input clock to I2C controller + * @i2c_clk: Maximum I2C clock speed + * @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 + */ +struct cdns_i2c { + void __iomem *membase; + struct i2c_adapter adap; + struct i2c_msg *p_msg; + int err_status; + struct completion xfer_done; + unsigned char *p_send_buf; + unsigned char *p_recv_buf; + u8 suspended; + unsigned int send_count; + unsigned int recv_count; + int irq; + unsigned long input_clk; + unsigned int i2c_clk; + unsigned int bus_hold_flag; + struct clk *clk; + struct notifier_block clk_rate_change_nb; +}; + +#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \ + clk_rate_change_nb) + +/** + * cdns_i2c_clear_bus_hold() - Clear bus hold bit + * @id: Pointer to driver data struct + * + * Helper to clear the controller's bus hold bit. + */ +static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id) +{ + u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); + if (reg & CDNS_I2C_CR_HOLD) + cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET); +} + +/** + * cdns_i2c_isr - Interrupt handler for the I2C device + * @irq: irq number for the I2C device + * @ptr: void pointer to cdns_i2c structure + * + * This function handles the data interrupt, transfer complete interrupt and + * the error interrupts of the I2C device. + * + * Return: IRQ_HANDLED always + */ +static irqreturn_t cdns_i2c_isr(int irq, void *ptr) +{ + unsigned int isr_status, avail_bytes; + unsigned int bytes_to_recv, bytes_to_send; + struct cdns_i2c *id = ptr; + /* Signal completion only after everything is updated */ + int done_flag = 0; + irqreturn_t status = IRQ_NONE; + + isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET); + + /* Handling nack and arbitration lost interrupt */ + if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) { + done_flag = 1; + status = IRQ_HANDLED; + } + + /* Handling Data interrupt */ + if ((isr_status & CDNS_I2C_IXR_DATA) && + (id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) { + /* Always read data interrupt threshold bytes */ + bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH; + id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH; + avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET); + + /* + * if the tranfer size register value is zero, then + * check for the remaining bytes and update the + * transfer size register. + */ + if (!avail_bytes) { + if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) + cdns_i2c_writere |