From 1c2d721a7063bc3b8668de07f8d14dfbac4bf515 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 2 Jun 2013 23:05:03 +0000 Subject: i2c: intel-mid: remove obsolete driver Moorestown support is removed from kernel and Medfield is supported by i2c-designware-pci. But i2c-intel-mid is still in upstream and community resources are wasted to maintain it. Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/i2c/busses/Kconfig') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 631736e2e7ed..94364d41686f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -474,16 +474,6 @@ config I2C_IMX This driver can also be built as a module. If so, the module will be called i2c-imx. -config I2C_INTEL_MID - tristate "Intel Moorestown/Medfield Platform I2C controller" - depends on PCI - help - Say Y here if you have an Intel Moorestown/Medfield platform I2C - controller. - - This support is also available as a module. If so, the module - will be called i2c-intel-mid. - config I2C_IOP3XX tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface" depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX -- cgit v1.2.3 From 560746eb79d3124a278452c8dd968682b521cc82 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 15 Jun 2013 09:52:16 +1200 Subject: i2c: vt8500: Add support for I2C bus on Wondermedia SoCs This patch adds support for the I2C bus controllers found on Wondermedia 8xxx-series SoCs. Only master-mode is supported. Signed-off-by: Tony Prisk [wsa: fixed one macro to shift 8 instead of 16] Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-vt8500.txt | 24 ++ MAINTAINERS | 1 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-wmt.c | 479 +++++++++++++++++++++ 5 files changed, 515 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt create mode 100644 drivers/i2c/busses/i2c-wmt.c (limited to 'drivers/i2c/busses/Kconfig') diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt new file mode 100644 index 000000000000..94a425eaa6c7 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt @@ -0,0 +1,24 @@ +* Wondermedia I2C Controller + +Required properties : + + - compatible : should be "wm,wm8505-i2c" + - reg : Offset and length of the register set for the device + - interrupts : where IRQ is the interrupt number + - clocks : phandle to the I2C clock source + +Optional properties : + + - clock-frequency : desired I2C bus clock frequency in Hz. + Valid values are 100000 and 400000. + Default to 100000 if not specified, or invalid value. + +Example : + + i2c_0: i2c@d8280000 { + compatible = "wm,wm8505-i2c"; + reg = <0xd8280000 0x1000>; + interrupts = <19>; + clocks = <&clki2c0>; + clock-frequency = <400000>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f35a259a6564..5fa200fa5b24 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1285,6 +1285,7 @@ S: Maintained F: arch/arm/mach-vt8500/ F: drivers/clocksource/vt8500_timer.c F: drivers/gpio/gpio-vt8500.c +F: drivers/i2c/busses/i2c-wmt.c F: drivers/mmc/host/wmt-sdmmc.c F: drivers/pwm/pwm-vt8500.c F: drivers/rtc/rtc-vt8500.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 94364d41686f..6582611bfee6 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -714,6 +714,16 @@ config I2C_VERSATILE This driver can also be built as a module. If so, the module will be called i2c-versatile. +config I2C_WMT + tristate "Wondermedia WM8xxx SoC I2C bus support" + depends on ARCH_VT8500 + help + Say yes if you want to support the I2C bus on Wondermedia 8xxx-series + SoCs. + + This driver can also be built as a module. If so, the module will be + called i2c-wmt. + config I2C_OCTEON tristate "Cavium OCTEON I2C bus support" depends on CPU_CAVIUM_OCTEON diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3e07dc53eeae..385f99dd1b45 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_WMT) += i2c-wmt.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c new file mode 100644 index 000000000000..baaa7d15b73e --- /dev/null +++ b/drivers/i2c/busses/i2c-wmt.c @@ -0,0 +1,479 @@ +/* + * Wondermedia I2C Master Mode Driver + * + * Copyright (C) 2012 Tony Prisk + * + * Derived from GPLv2+ licensed source: + * - Copyright (C) 2008 WonderMedia Technologies, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, or + * (at your option) any later version. as published by the Free Software + * Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CR 0x00 +#define REG_TCR 0x02 +#define REG_CSR 0x04 +#define REG_ISR 0x06 +#define REG_IMR 0x08 +#define REG_CDR 0x0A +#define REG_TR 0x0C +#define REG_MCR 0x0E +#define REG_SLAVE_CR 0x10 +#define REG_SLAVE_SR 0x12 +#define REG_SLAVE_ISR 0x14 +#define REG_SLAVE_IMR 0x16 +#define REG_SLAVE_DR 0x18 +#define REG_SLAVE_TR 0x1A + +/* REG_CR Bit fields */ +#define CR_TX_NEXT_ACK 0x0000 +#define CR_ENABLE 0x0001 +#define CR_TX_NEXT_NO_ACK 0x0002 +#define CR_TX_END 0x0004 +#define CR_CPU_RDY 0x0008 +#define SLAV_MODE_SEL 0x8000 + +/* REG_TCR Bit fields */ +#define TCR_STANDARD_MODE 0x0000 +#define TCR_MASTER_WRITE 0x0000 +#define TCR_HS_MODE 0x2000 +#define TCR_MASTER_READ 0x4000 +#define TCR_FAST_MODE 0x8000 +#define TCR_SLAVE_ADDR_MASK 0x007F + +/* REG_ISR Bit fields */ +#define ISR_NACK_ADDR 0x0001 +#define ISR_BYTE_END 0x0002 +#define ISR_SCL_TIMEOUT 0x0004 +#define ISR_WRITE_ALL 0x0007 + +/* REG_IMR Bit fields */ +#define IMR_ENABLE_ALL 0x0007 + +/* REG_CSR Bit fields */ +#define CSR_RCV_NOT_ACK 0x0001 +#define CSR_RCV_ACK_MASK 0x0001 +#define CSR_READY_MASK 0x0002 + +/* REG_TR */ +#define SCL_TIMEOUT(x) (((x) & 0xFF) << 8) +#define TR_STD 0x0064 +#define TR_HS 0x0019 + +/* REG_MCR */ +#define MCR_APB_96M 7 +#define MCR_APB_166M 12 + +#define I2C_MODE_STANDARD 0 +#define I2C_MODE_FAST 1 + +#define WMT_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +struct wmt_i2c_dev { + struct i2c_adapter adapter; + struct completion complete; + struct device *dev; + void __iomem *base; + struct clk *clk; + int mode; + int irq; + u16 cmd_status; +}; + +static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev) +{ + unsigned long timeout; + + timeout = jiffies + WMT_I2C_TIMEOUT; + while (!(readw(i2c_dev->base + REG_CSR) & CSR_READY_MASK)) { + if (time_after(jiffies, timeout)) { + dev_warn(i2c_dev->dev, "timeout waiting for bus ready\n"); + return -EBUSY; + } + msleep(20); + } + + return 0; +} + +static int wmt_check_status(struct wmt_i2c_dev *i2c_dev) +{ + int ret = 0; + + if (i2c_dev->cmd_status & ISR_NACK_ADDR) + ret = -EIO; + + if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT) + ret = -ETIMEDOUT; + + return ret; +} + +static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, + int last) +{ + struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + u16 val, tcr_val; + int ret, wait_result; + int xfer_len = 0; + + if (!(pmsg->flags & I2C_M_NOSTART)) { + ret = wmt_i2c_wait_bus_not_busy(i2c_dev); + if (ret < 0) + return ret; + } + + if (pmsg->len == 0) { + /* + * We still need to run through the while (..) once, so + * start at -1 and break out early from the loop + */ + xfer_len = -1; + writew(0, i2c_dev->base + REG_CDR); + } else { + writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR); + } + + if (!(pmsg->flags & I2C_M_NOSTART)) { + val = readw(i2c_dev->base + REG_CR); + val &= ~CR_TX_END; + writew(val, i2c_dev->base + REG_CR); + + val = readw(i2c_dev->base + REG_CR); + val |= CR_CPU_RDY; + writew(val, i2c_dev->base + REG_CR); + } + + INIT_COMPLETION(i2c_dev->complete); + + if (i2c_dev->mode == I2C_MODE_STANDARD) + tcr_val = TCR_STANDARD_MODE; + else + tcr_val = TCR_FAST_MODE; + + tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK)); + + writew(tcr_val, i2c_dev->base + REG_TCR); + + if (pmsg->flags & I2C_M_NOSTART) { + val = readw(i2c_dev->base + REG_CR); + val |= CR_CPU_RDY; + writew(val, i2c_dev->base + REG_CR); + } + + while (xfer_len < pmsg->len) { + wait_result = wait_for_completion_timeout(&i2c_dev->complete, + 500 * HZ / 1000); + + if (wait_result == 0) + return -ETIMEDOUT; + + ret = wmt_check_status(i2c_dev); + if (ret) + return ret; + + xfer_len++; + + val = readw(i2c_dev->base + REG_CSR); + if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) { + dev_dbg(i2c_dev->dev, "write RCV NACK error\n"); + return -EIO; + } + + if (pmsg->len == 0) { + val = CR_TX_END | CR_CPU_RDY | CR_ENABLE; + writew(val, i2c_dev->base + REG_CR); + break; + } + + if (xfer_len == pmsg->len) { + if (last != 1) + writew(CR_ENABLE, i2c_dev->base + REG_CR); + } else { + writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base + + REG_CDR); + writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR); + } + } + + return 0; +} + +static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, + int last) +{ + struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + u16 val, tcr_val; + int ret, wait_result; + u32 xfer_len = 0; + + if (!(pmsg->flags & I2C_M_NOSTART)) { + ret = wmt_i2c_wait_bus_not_busy(i2c_dev); + if (ret < 0) + return ret; + } + + val = readw(i2c_dev->base + REG_CR); + val &= ~CR_TX_END; + writew(val, i2c_dev->base + REG_CR); + + val = readw(i2c_dev->base + REG_CR); + val &= ~CR_TX_NEXT_NO_ACK; + writew(val, i2c_dev->base + REG_CR); + + if (!(pmsg->flags & I2C_M_NOSTART)) { + val = readw(i2c_dev->base + REG_CR); + val |= CR_CPU_RDY; + writew(val, i2c_dev->base + REG_CR); + } + + if (pmsg->len == 1) { + val = readw(i2c_dev->base + REG_CR); + val |= CR_TX_NEXT_NO_ACK; + writew(val, i2c_dev->base + REG_CR); + } + + INIT_COMPLETION(i2c_dev->complete); + + if (i2c_dev->mode == I2C_MODE_STANDARD) + tcr_val = TCR_STANDARD_MODE; + else + tcr_val = TCR_FAST_MODE; + + tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK); + + writew(tcr_val, i2c_dev->base + REG_TCR); + + if (pmsg->flags & I2C_M_NOSTART) { + val = readw(i2c_dev->base + REG_CR); + val |= CR_CPU_RDY; + writew(val, i2c_dev->base + REG_CR); + } + + while (xfer_len < pmsg->len) { + wait_result = wait_for_completion_timeout(&i2c_dev->complete, + 500 * HZ / 1000); + + if (!wait_result) + return -ETIMEDOUT; + + ret = wmt_check_status(i2c_dev); + if (ret) + return ret; + + pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8; + xfer_len++; + + if (xfer_len == pmsg->len - 1) { + val = readw(i2c_dev->base + REG_CR); + val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY); + writew(val, i2c_dev->base + REG_CR); + } else { + val = readw(i2c_dev->base + REG_CR); + val |= CR_CPU_RDY; + writew(val, i2c_dev->base + REG_CR); + } + } + + return 0; +} + +static int wmt_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_msg *pmsg; + int i, is_last; + int ret = 0; + + for (i = 0; ret >= 0 && i < num; i++) { + is_last = ((i + 1) == num); + + pmsg = &msgs[i]; + if (pmsg->flags & I2C_M_RD) + ret = wmt_i2c_read(adap, pmsg, is_last); + else + ret = wmt_i2c_write(adap, pmsg, is_last); + } + + return (ret < 0) ? ret : i; +} + +static u32 wmt_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm wmt_i2c_algo = { + .master_xfer = wmt_i2c_xfer, + .functionality = wmt_i2c_func, +}; + +static irqreturn_t wmt_i2c_isr(int irq, void *data) +{ + struct wmt_i2c_dev *i2c_dev = data; + + /* save the status and write-clear it */ + i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR); + writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR); + + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev) +{ + int err; + + err = clk_prepare_enable(i2c_dev->clk); + if (err) { + dev_err(i2c_dev->dev, "failed to enable clock\n"); + return err; + } + + err = clk_set_rate(i2c_dev->clk, 20000000); + if (err) { + dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n"); + return err; + } + + writew(0, i2c_dev->base + REG_CR); + writew(MCR_APB_166M, i2c_dev->base + REG_MCR); + writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR); + writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR); + writew(CR_ENABLE, i2c_dev->base + REG_CR); + readw(i2c_dev->base + REG_CSR); /* read clear */ + writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR); + + if (i2c_dev->mode == I2C_MODE_STANDARD) + writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR); + else + writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR); + + return 0; +} + +static int wmt_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct wmt_i2c_dev *i2c_dev; + struct i2c_adapter *adap; + struct resource *res; + int err; + u32 clk_rate; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + dev_err(&pdev->dev, "device memory allocation failed\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c_dev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + i2c_dev->irq = irq_of_parse_and_map(np, 0); + if (!i2c_dev->irq) { + dev_err(&pdev->dev, "irq missing or invalid\n"); + return -EINVAL; + } + + i2c_dev->clk = of_clk_get(np, 0); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "unable to request clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + i2c_dev->mode = I2C_MODE_STANDARD; + err = of_property_read_u32(np, "clock-frequency", &clk_rate); + if ((!err) && (clk_rate == 400000)) + i2c_dev->mode = I2C_MODE_FAST; + + i2c_dev->dev = &pdev->dev; + + err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0, + "i2c", i2c_dev); + if (err) { + dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq); + return err; + } + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); + strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &wmt_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + err = wmt_i2c_reset_hardware(i2c_dev); + if (err) { + dev_err(&pdev->dev, "error initializing hardware\n"); + return err; + } + + err = i2c_add_adapter(adap); + if (err) { + dev_err(&pdev->dev, "failed to add adapter\n"); + return err; + } + + platform_set_drvdata(pdev, i2c_dev); + + of_i2c_register_devices(adap); + + return 0; +} + +static int wmt_i2c_remove(struct platform_device *pdev) +{ + struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + /* Disable interrupts, clock and delete adapter */ + writew(0, i2c_dev->base + REG_IMR); + clk_disable_unprepare(i2c_dev->clk); + i2c_del_adapter(&i2c_dev->adapter); + + return 0; +} + +static struct of_device_id wmt_i2c_dt_ids[] = { + { .compatible = "wm,wm8505-i2c" }, + { /* Sentinel */ }, +}; + +static struct platform_driver wmt_i2c_driver = { + .probe = wmt_i2c_probe, + .remove = wmt_i2c_remove, + .driver = { + .name = "wmt-i2c", + .owner = THIS_MODULE, + .of_match_table = wmt_i2c_dt_ids, + }, +}; + +module_platform_driver(wmt_i2c_driver); + +MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter"); +MODULE_AUTHOR("Tony Prisk "); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids); -- cgit v1.2.3 From 3d66ac7d81ac70dfaab8a573f7ad2be94f7d6da3 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 12 Jun 2013 18:53:32 +0200 Subject: i2c: mv64xxx: Add Allwinner sun4i compatible Add the compatible string for the Allwinner A10 i2c controller and the associated register layout. Signed-off-by: Maxime Ripard Tested-by: Sebastian Hesselbarth Tested-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 3 ++- drivers/i2c/busses/i2c-mv64xxx.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/i2c/busses/Kconfig') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 6582611bfee6..96c6d82da3ed 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -497,10 +497,11 @@ config I2C_MPC config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on (MV64X60 || PLAT_ORION) + 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. + This driver is also used for Allwinner SoCs I2C controllers. This driver can also be built as a module. If so, the module will be called i2c-mv64xxx. diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 7ba9bac18478..7a0e39b7f928 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -129,6 +129,16 @@ static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { .soft_reset = 0x1c, }; +static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_sun4i = { + .addr = 0x00, + .ext_addr = 0x04, + .data = 0x08, + .control = 0x0c, + .status = 0x10, + .clock = 0x14, + .soft_reset = 0x18, +}; + static void mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg) @@ -509,6 +519,7 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = { ***************************************************************************** */ static const struct of_device_id mv64xxx_i2c_of_match_table[] = { + { .compatible = "allwinner,sun4i-i2c", .data = &mv64xxx_i2c_regs_sun4i}, { .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx}, {} }; -- cgit v1.2.3 From f39901c1befa556bc91902516a3e2e460000b4a8 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Wed, 19 Jun 2013 16:59:57 -0700 Subject: i2c: i801: SMBus patch for Intel Coleto Creek DeviceIDs This patch adds the i801 SMBus Controller DeviceIDs for the Intel Coleto Creek PCH. Signed-off-by: Seth Heasley Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers/i2c/busses/Kconfig') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index d55b8ab2d10f..d29dea0f3232 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -24,6 +24,7 @@ Supported adapters: * Intel Lynx Point-LP (PCH) * Intel Avoton (SOC) * Intel Wellsburg (PCH) + * Intel Coleto Creek (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 96c6d82da3ed..b865c8979aec 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -108,6 +108,7 @@ config I2C_I801 Lynx Point-LP (PCH) Avoton (SOC) Wellsburg (PCH) + Coleto Creek (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 3a6903f63913..4ebceed6bc66 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -58,6 +58,7 @@ Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes + Coleto Creek (PCH) 0x23b0 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -169,6 +170,7 @@ #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 #define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 +#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 #define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22 @@ -817,6 +819,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) }, { 0, } }; -- cgit v1.2.3 From e0b9b7b06704eab2b95372a7c8daf9c0cce46bd0 Mon Sep 17 00:00:00 2001 From: Kevin Strasser Date: Sun, 23 Jun 2013 21:00:04 -0700 Subject: i2c: Kontron PLD i2c bus driver Add i2c support for the on-board PLD found on some Kontron embedded modules. Originally-From: Michael Brunner Signed-off-by: Kevin Strasser Acked-by: Guenter Roeck Acked-by: Darren Hart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-kempld.c | 410 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 drivers/i2c/busses/i2c-kempld.c (limited to 'drivers/i2c/busses/Kconfig') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index b865c8979aec..3c046097be71 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -485,6 +485,16 @@ config I2C_IOP3XX This driver can also be built as a module. If so, the module will be called i2c-iop3xx. +config I2C_KEMPLD + tristate "Kontron COM I2C Controller" + depends on MFD_KEMPLD + help + This enables support for the I2C bus interface on some Kontron ETX + and COMexpress (ETXexpress) modules. + + This driver can also be built as a module. If so, the module + will be called i2c-kempld. + config I2C_MPC tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 385f99dd1b45..d00997f3eb3b 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.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_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-kempld.c b/drivers/i2c/busses/i2c-kempld.c new file mode 100644 index 000000000000..ccec916bc3eb --- /dev/null +++ b/drivers/i2c/busses/i2c-kempld.c @@ -0,0 +1,410 @@ +/* + * I2C bus driver for Kontron COM modules + * + * Copyright (c) 2010-2013 Kontron Europe GmbH + * Author: Michael Brunner + * + * The driver is based on the i2c-ocores driver by Peter Korsgaard. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define KEMPLD_I2C_PRELOW 0x0b +#define KEMPLD_I2C_PREHIGH 0x0c +#define KEMPLD_I2C_DATA 0x0e + +#define KEMPLD_I2C_CTRL 0x0d +#define I2C_CTRL_IEN 0x40 +#define I2C_CTRL_EN 0x80 + +#define KEMPLD_I2C_STAT 0x0f +#define I2C_STAT_IF 0x01 +#define I2C_STAT_TIP 0x02 +#define I2C_STAT_ARBLOST 0x20 +#define I2C_STAT_BUSY 0x40 +#define I2C_STAT_NACK 0x80 + +#define KEMPLD_I2C_CMD 0x0f +#define I2C_CMD_START 0x91 +#define I2C_CMD_STOP 0x41 +#define I2C_CMD_READ 0x21 +#define I2C_CMD_WRITE 0x11 +#define I2C_CMD_READ_ACK 0x21 +#define I2C_CMD_READ_NACK 0x29 +#define I2C_CMD_IACK 0x01 + +#define KEMPLD_I2C_FREQ_MAX 2700 /* 2.7 mHz */ +#define KEMPLD_I2C_FREQ_STD 100 /* 100 kHz */ + +enum { + STATE_DONE = 0, + STATE_INIT, + STATE_ADDR, + STATE_ADDR10, + STATE_START, + STATE_WRITE, + STATE_READ, + STATE_ERROR, +}; + +struct kempld_i2c_data { + struct device *dev; + struct kempld_device_data *pld; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; + bool was_active; +}; + +static unsigned int bus_frequency = KEMPLD_I2C_FREQ_STD; +module_param(bus_frequency, uint, 0); +MODULE_PARM_DESC(bus_frequency, "Set I2C bus frequency in kHz (default=" + __MODULE_STRING(KEMPLD_I2C_FREQ_STD)")"); + +static int i2c_bus = -1; +module_param(i2c_bus, int, 0); +MODULE_PARM_DESC(i2c_bus, "Set I2C bus number (default=-1 for dynamic assignment)"); + +static bool i2c_gpio_mux; +module_param(i2c_gpio_mux, bool, 0); +MODULE_PARM_DESC(i2c_gpio_mux, "Enable I2C port on GPIO out (default=false)"); + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static int kempld_i2c_process(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u8 stat = kempld_read8(pld, KEMPLD_I2C_STAT); + struct i2c_msg *msg = i2c->msg; + u8 addr; + + /* Ready? */ + if (stat & I2C_STAT_TIP) + return -EBUSY; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* Stop has been sent */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + if (i2c->state == STATE_ERROR) + return -EIO; + return 0; + } + + /* Error? */ + if (stat & I2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -EAGAIN; + } + + if (i2c->state == STATE_INIT) { + if (stat & I2C_STAT_BUSY) + return -EBUSY; + + i2c->state = STATE_ADDR; + } + + if (i2c->state == STATE_ADDR) { + /* 10 bit address? */ + if (i2c->msg->flags & I2C_M_TEN) { + addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6); + i2c->state = STATE_ADDR10; + } else { + addr = (i2c->msg->addr << 1); + i2c->state = STATE_START; + } + + /* Set read bit if necessary */ + addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0; + + kempld_write8(pld, KEMPLD_I2C_DATA, addr); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START); + + return 0; + } + + /* Second part of 10 bit addressing */ + if (i2c->state == STATE_ADDR10) { + kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); + + i2c->state = STATE_START; + return 0; + } + + if (i2c->state == STATE_START || i2c->state == STATE_WRITE) { + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & I2C_STAT_NACK) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -ENXIO; + } + } else { + msg->buf[i2c->pos++] = kempld_read8(pld, KEMPLD_I2C_DATA); + } + + if (i2c->pos >= msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { + if (!(msg->flags & I2C_M_NOSTART)) { + i2c->state = STATE_ADDR; + return 0; + } else { + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } + } else { + i2c->state = STATE_DONE; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return 0; + } + } + + if (i2c->state == STATE_READ) { + kempld_write8(pld, KEMPLD_I2C_CMD, i2c->pos == (msg->len - 1) ? + I2C_CMD_READ_NACK : I2C_CMD_READ_ACK); + } else { + kempld_write8(pld, KEMPLD_I2C_DATA, msg->buf[i2c->pos++]); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); + } + + return 0; +} + +static int kempld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct kempld_i2c_data *i2c = i2c_get_adapdata(adap); + struct kempld_device_data *pld = i2c->pld; + unsigned long timeout = jiffies + HZ; + int ret; + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_INIT; + + /* Handle the transfer */ + while (time_before(jiffies, timeout)) { + kempld_get_mutex(pld); + ret = kempld_i2c_process(i2c); + kempld_release_mutex(pld); + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) + return (i2c->state == STATE_DONE) ? num : ret; + + if (ret == 0) + timeout = jiffies + HZ; + + usleep_range(5, 15); + } + + i2c->state = STATE_ERROR; + + return -ETIMEDOUT; +} + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static void kempld_i2c_device_init(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u16 prescale_corr; + long prescale; + u8 ctrl; + u8 stat; + u8 cfg; + + /* Make sure the device is disabled */ + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~(I2C_CTRL_EN | I2C_CTRL_IEN); + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + if (bus_frequency > KEMPLD_I2C_FREQ_MAX) + bus_frequency = KEMPLD_I2C_FREQ_MAX; + + if (pld->info.spec_major == 1) + prescale = pld->pld_clock / bus_frequency * 5 - 1000; + else + prescale = pld->pld_clock / bus_frequency * 4 - 3000; + + if (prescale < 0) + prescale = 0; + + /* Round to the best matching value */ + prescale_corr = prescale / 1000; + if (prescale % 1000 >= 500) + prescale_corr++; + + kempld_write8(pld, KEMPLD_I2C_PRELOW, prescale_corr & 0xff); + kempld_write8(pld, KEMPLD_I2C_PREHIGH, prescale_corr >> 8); + + /* Activate I2C bus output on GPIO pins */ + cfg = kempld_read8(pld, KEMPLD_CFG); + if (i2c_gpio_mux) + cfg |= KEMPLD_CFG_GPIO_I2C_MUX; + else + cfg &= ~KEMPLD_CFG_GPIO_I2C_MUX; + kempld_write8(pld, KEMPLD_CFG, cfg); + + /* Enable the device */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + ctrl |= I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + stat = kempld_read8(pld, KEMPLD_I2C_STAT); + if (stat & I2C_STAT_BUSY) + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); +} + +static u32 kempld_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm kempld_i2c_algorithm = { + .master_xfer = kempld_i2c_xfer, + .functionality = kempld_i2c_func, +}; + +static struct i2c_adapter kempld_i2c_adapter = { + .owner = THIS_MODULE, + .name = "i2c-kempld", + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &kempld_i2c_algorithm, +}; + +static int kempld_i2c_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); + struct kempld_i2c_data *i2c; + int ret; + u8 ctrl; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->pld = pld; + i2c->dev = &pdev->dev; + i2c->adap = kempld_i2c_adapter; + i2c->adap.dev.parent = i2c->dev; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + + if (ctrl & I2C_CTRL_EN) + i2c->was_active = true; + + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + /* Add I2C adapter to I2C tree */ + if (i2c_bus >= -1) + i2c->adap.nr = i2c_bus; + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret) + return ret; + + dev_info(i2c->dev, "I2C bus initialized at %dkHz\n", + bus_frequency); + + return 0; +} + +static int kempld_i2c_remove(struct platform_device *pdev) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + u8 ctrl; + + kempld_get_mutex(pld); + /* + * Disable I2C logic if it was not activated before the + * driver loaded + */ + if (!i2c->was_active) { + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + } + kempld_release_mutex(pld); + + i2c_del_adapter(&i2c->adap); + + return 0; +} + +#ifdef CONFIG_PM +static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + u8 ctrl; + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_i2c_resume(struct platform_device *pdev) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + + kempld_get_mutex(pld); + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + return 0; +} +#else +#define kempld_i2c_suspend NULL +#define kempld_i2c_resume NULL +#endif + +static struct platform_driver kempld_i2c_driver = { + .driver = { + .name = "kempld-i2c", + .owner = THIS_MODULE, + }, + .probe = kempld_i2c_probe, + .remove = kempld_i2c_remove, + .suspend = kempld_i2c_suspend, + .resume = kempld_i2c_resume, +}; + +module_platform_driver(kempld_i2c_driver); + +MODULE_DESCRIPTION("KEM PLD I2C Driver"); +MODULE_AUTHOR("Michael Brunner "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_i2c"); -- cgit v1.2.3