diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-07 15:56:25 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-07 15:56:25 -0800 |
commit | dd04265b028c00c365a78f9ff78a05e217f98656 (patch) | |
tree | 4df2ce8ca3a2b8fcf5742718ecb90abe68666ffb /drivers/i2c | |
parent | 6dc3eb5c1f96641cda7056aa34393e317076d6cf (diff) | |
parent | 3f4ae8605261e6f71e721e026bc18cb1851a7276 (diff) |
Merge branch 'for-linus/i2c' of git://git.fluff.org/bjdooks/linux
* 'for-linus/i2c' of git://git.fluff.org/bjdooks/linux:
i2c: Add support for Xilinx XPS IIC Bus Interface
i2c: omap: Add support for 16-bit registers
i2c-pnx: fix setting start/stop condition
powerpc: doc/dts-bindings: update doc of FSL I2C bindings
i2c-mpc: add support for the MPC512x processors from Freescale
i2c-mpc: rename "setclock" initialization functions to "setup"
i2c-mpc: use __devinit[data] for initialization functions and data
i2c/imx: don't add probe function to the driver struct
i2c: Add support for Ux500/Nomadik I2C controller
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 24 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 2 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mpc.c | 194 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-nomadik.c | 959 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 44 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pnx.c | 11 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-xiic.c | 824 |
8 files changed, 1951 insertions, 108 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4cd1ff77ced..9c6170cd9aa 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -421,13 +421,12 @@ config I2C_IXP2000 instead. config I2C_MPC - tristate "MPC107/824x/85xx/52xx/86xx" + tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC32 help If you say yes to this option, support will be included for the - built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and - MPC85xx/MPC8641 family processors. The driver may also work on 52xx - family processors, though interrupts are known not to work. + built-in I2C interface on the MPC107, Tsi107, MPC512x, MPC52xx, + MPC8240, MPC8245, MPC83xx, MPC85xx and MPC8641 family processors. This driver can also be built as a module. If so, the module will be called i2c-mpc. @@ -442,6 +441,13 @@ config I2C_MV64XXX This driver can also be built as a module. If so, the module will be called i2c-mv64xxx. +config I2C_NOMADIK + tristate "ST-Ericsson Nomadik/Ux500 I2C Controller" + depends on PLAT_NOMADIK + help + If you say yes to this option, support will be included for the + I2C interface from ST-Ericsson's Nomadik and Ux500 architectures. + config I2C_OCORES tristate "OpenCores I2C Controller" depends on EXPERIMENTAL @@ -577,6 +583,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_XILINX + tristate "Xilinx I2C Controller" + depends on EXPERIMENTAL && HAS_IOMEM + help + If you say yes to this option, support will be included for the + Xilinx I2C controller. + + This driver can also be built as a module. If so, the module + will be called xilinx_i2c. + comment "External I2C/SMBus adapter drivers" config I2C_PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index c2c4ea1908d..097236f631e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o +obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o @@ -55,6 +56,7 @@ obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 75bf820e7cc..32375bddae7 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -627,7 +627,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev) } static struct platform_driver i2c_imx_driver = { - .probe = i2c_imx_probe, .remove = __exit_p(i2c_imx_remove), .driver = { .name = DRIVER_NAME, diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index f627001108b..78a15af3294 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -31,6 +31,9 @@ #define DRV_NAME "mpc-i2c" +#define MPC_I2C_CLOCK_LEGACY 0 +#define MPC_I2C_CLOCK_PRESERVE (~0U) + #define MPC_I2C_FDR 0x04 #define MPC_I2C_CR 0x08 #define MPC_I2C_SR 0x0c @@ -66,10 +69,9 @@ struct mpc_i2c_divider { u16 fdr; /* including dfsrr */ }; -struct mpc_i2c_match_data { - void (*setclock)(struct device_node *node, - struct mpc_i2c *i2c, - u32 clock, u32 prescaler); +struct mpc_i2c_data { + void (*setup)(struct device_node *node, struct mpc_i2c *i2c, + u32 clock, u32 prescaler); u32 prescaler; }; @@ -164,8 +166,8 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) return 0; } -#ifdef CONFIG_PPC_MPC52xx -static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = { +#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x) +static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] __devinitconst = { {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23}, {28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02}, {36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28}, @@ -186,14 +188,15 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = { {10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f} }; -int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler) +static int __devinit mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, + int prescaler) { const struct mpc_i2c_divider *div = NULL; unsigned int pvr = mfspr(SPRN_PVR); u32 divider; int i; - if (!clock) + if (clock == MPC_I2C_CLOCK_LEGACY) return -EINVAL; /* Determine divider value */ @@ -215,12 +218,18 @@ int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler) return div ? (int)div->fdr : -EINVAL; } -static void mpc_i2c_setclock_52xx(struct device_node *node, - struct mpc_i2c *i2c, - u32 clock, u32 prescaler) +static void __devinit mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) { int ret, fdr; + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using fdr %d\n", + readb(i2c->base + MPC_I2C_FDR)); + return; + } + ret = mpc_i2c_get_fdr_52xx(node, clock, prescaler); fdr = (ret >= 0) ? ret : 0x3f; /* backward compatibility */ @@ -229,16 +238,52 @@ static void mpc_i2c_setclock_52xx(struct device_node *node, if (ret >= 0) dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr); } -#else /* !CONFIG_PPC_MPC52xx */ -static void mpc_i2c_setclock_52xx(struct device_node *node, - struct mpc_i2c *i2c, - u32 clock, u32 prescaler) +#else /* !(CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x) */ +static void __devinit mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ +} +#endif /* CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x */ + +#ifdef CONFIG_PPC_MPC512x +static void __devinit mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ + struct device_node *node_ctrl; + void __iomem *ctrl; + const u32 *pval; + u32 idx; + + /* Enable I2C interrupts for mpc5121 */ + node_ctrl = of_find_compatible_node(NULL, NULL, + "fsl,mpc5121-i2c-ctrl"); + if (node_ctrl) { + ctrl = of_iomap(node_ctrl, 0); + if (ctrl) { + /* Interrupt enable bits for i2c-0/1/2: bit 24/26/28 */ + pval = of_get_property(node, "reg", NULL); + idx = (*pval & 0xff) / 0x20; + setbits32(ctrl, 1 << (24 + idx * 2)); + iounmap(ctrl); + } + of_node_put(node_ctrl); + } + + /* The clock setup for the 52xx works also fine for the 512x */ + mpc_i2c_setup_52xx(node, i2c, clock, prescaler); +} +#else /* CONFIG_PPC_MPC512x */ +static void __devinit mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) { } -#endif /* CONFIG_PPC_MPC52xx*/ +#endif /* CONFIG_PPC_MPC512x */ #ifdef CONFIG_FSL_SOC -static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = { +static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] __devinitconst = { {160, 0x0120}, {192, 0x0121}, {224, 0x0122}, {256, 0x0123}, {288, 0x0100}, {320, 0x0101}, {352, 0x0601}, {384, 0x0102}, {416, 0x0602}, {448, 0x0126}, {480, 0x0103}, {512, 0x0127}, @@ -258,7 +303,7 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = { {49152, 0x011e}, {61440, 0x011f} }; -u32 mpc_i2c_get_sec_cfg_8xxx(void) +static u32 __devinit mpc_i2c_get_sec_cfg_8xxx(void) { struct device_node *node = NULL; u32 __iomem *reg; @@ -287,13 +332,14 @@ u32 mpc_i2c_get_sec_cfg_8xxx(void) return val; } -int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler) +static int __devinit mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, + u32 prescaler) { const struct mpc_i2c_divider *div = NULL; u32 divider; int i; - if (!clock) + if (clock == MPC_I2C_CLOCK_LEGACY) return -EINVAL; /* Determine proper divider value */ @@ -320,12 +366,19 @@ int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler) return div ? (int)div->fdr : -EINVAL; } -static void mpc_i2c_setclock_8xxx(struct device_node *node, - struct mpc_i2c *i2c, - u32 clock, u32 prescaler) +static void __devinit mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) { int ret, fdr; + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using dfsrr %d, fdr %d\n", + readb(i2c->base + MPC_I2C_DFSRR), + readb(i2c->base + MPC_I2C_FDR)); + return; + } + ret = mpc_i2c_get_fdr_8xxx(node, clock, prescaler); fdr = (ret >= 0) ? ret : 0x1031; /* backward compatibility */ @@ -338,9 +391,9 @@ static void mpc_i2c_setclock_8xxx(struct device_node *node, } #else /* !CONFIG_FSL_SOC */ -static void mpc_i2c_setclock_8xxx(struct device_node *node, - struct mpc_i2c *i2c, - u32 clock, u32 prescaler) +static void __devinit mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) { } #endif /* CONFIG_FSL_SOC */ @@ -494,7 +547,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op, { struct mpc_i2c *i2c; const u32 *prop; - u32 clock = 0; + u32 clock = MPC_I2C_CLOCK_LEGACY; int result = 0; int plen; @@ -523,21 +576,21 @@ static int __devinit fsl_i2c_probe(struct of_device *op, } } - if (!of_get_property(op->node, "fsl,preserve-clocking", NULL)) { + if (of_get_property(op->node, "fsl,preserve-clocking", NULL)) { + clock = MPC_I2C_CLOCK_PRESERVE; + } else { prop = of_get_property(op->node, "clock-frequency", &plen); if (prop && plen == sizeof(u32)) clock = *prop; + } - if (match->data) { - struct mpc_i2c_match_data *data = - (struct mpc_i2c_match_data *)match->data; - data->setclock(op->node, i2c, clock, data->prescaler); - } else { - /* Backwards compatibility */ - if (of_get_property(op->node, "dfsrr", NULL)) - mpc_i2c_setclock_8xxx(op->node, i2c, - clock, 0); - } + if (match->data) { + struct mpc_i2c_data *data = match->data; + data->setup(op->node, i2c, clock, data->prescaler); + } else { + /* Backwards compatibility */ + if (of_get_property(op->node, "dfsrr", NULL)) + mpc_i2c_setup_8xxx(op->node, i2c, clock, 0); } dev_set_drvdata(&op->dev, i2c); @@ -582,47 +635,42 @@ static int __devexit fsl_i2c_remove(struct of_device *op) return 0; }; +static struct mpc_i2c_data mpc_i2c_data_512x __devinitdata = { + .setup = mpc_i2c_setup_512x, +}; + +static struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = { + .setup = mpc_i2c_setup_52xx, +}; + +static struct mpc_i2c_data mpc_i2c_data_8313 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, +}; + +static struct mpc_i2c_data mpc_i2c_data_8543 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, + .prescaler = 2, +}; + +static struct mpc_i2c_data mpc_i2c_data_8544 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, + .prescaler = 3, +}; + static const struct of_device_id mpc_i2c_of_match[] = { - {.compatible = "mpc5200-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_52xx, - }, - }, - {.compatible = "fsl,mpc5200b-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_52xx, - }, - }, - {.compatible = "fsl,mpc5200-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_52xx, - }, - }, - {.compatible = "fsl,mpc8313-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_8xxx, - }, - }, - {.compatible = "fsl,mpc8543-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_8xxx, - .prescaler = 2, - }, - }, - {.compatible = "fsl,mpc8544-i2c", - .data = &(struct mpc_i2c_match_data) { - .setclock = mpc_i2c_setclock_8xxx, - .prescaler = 3, - }, + {.compatible = "mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200b-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5121-i2c", .data = &mpc_i2c_data_512x, }, + {.compatible = "fsl,mpc8313-i2c", .data = &mpc_i2c_data_8313, }, + {.compatible = "fsl,mpc8543-i2c", .data = &mpc_i2c_data_8543, }, + {.compatible = "fsl,mpc8544-i2c", .data = &mpc_i2c_data_8544, }, /* Backward compatibility */ - }, {.compatible = "fsl-i2c", }, {}, }; - MODULE_DEVICE_TABLE(of, mpc_i2c_of_match); - /* Structure for a device driver */ static struct of_platform_driver mpc_i2c_driver = { .match_table = mpc_i2c_of_match, @@ -655,5 +703,5 @@ module_exit(fsl_i2c_exit); MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>"); MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and " - "MPC824x/85xx/52xx processors"); + "MPC824x/83xx/85xx/86xx/512x/52xx processors"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c new file mode 100644 index 00000000000..a15f731fa45 --- /dev/null +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -0,0 +1,959 @@ +/* + * Copyright (C) 2009 ST-Ericsson + * Copyright (C) 2009 STMicroelectronics + * + * I2C master mode controller driver, used in Nomadik 8815 + * and Ux500 platforms. + * + * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> + * Author: Sachin Verma <sachin.verma@st.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/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <plat/i2c.h> + +#define DRIVER_NAME "nmk-i2c" + +/* I2C Controller register offsets */ +#define I2C_CR (0x000) +#define I2C_SCR (0x004) +#define I2C_HSMCR (0x008) +#define I2C_MCR (0x00C) +#define I2C_TFR (0x010) +#define I2C_SR (0x014) +#define I2C_RFR (0x018) +#define I2C_TFTR (0x01C) +#define I2C_RFTR (0x020) +#define I2C_DMAR (0x024) +#define I2C_BRCR (0x028) +#define I2C_IMSCR (0x02C) +#define I2C_RISR (0x030) +#define I2C_MISR (0x034) +#define I2C_ICR (0x038) + +/* Control registers */ +#define I2C_CR_PE (0x1 << 0) /* Peripheral Enable */ +#define I2C_CR_OM (0x3 << 1) /* Operating mode */ +#define I2C_CR_SAM (0x1 << 3) /* Slave addressing mode */ +#define I2C_CR_SM (0x3 << 4) /* Speed mode */ +#define I2C_CR_SGCM (0x1 << 6) /* Slave general call mode */ +#define I2C_CR_FTX (0x1 << 7) /* Flush Transmit */ +#define I2C_CR_FRX (0x1 << 8) /* Flush Receive */ +#define I2C_CR_DMA_TX_EN (0x1 << 9) /* DMA Tx enable */ +#define I2C_CR_DMA_RX_EN (0x1 << 10) /* DMA Rx Enable */ +#define I2C_CR_DMA_SLE (0x1 << 11) /* DMA sync. logic enable */ +#define I2C_CR_LM (0x1 << 12) /* Loopback mode */ +#define I2C_CR_FON (0x3 << 13) /* Filtering on */ +#define I2C_CR_FS (0x3 << 15) /* Force stop enable */ + +/* Master controller (MCR) register */ +#define I2C_MCR_OP (0x1 << 0) /* Operation */ +#define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */ +#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */ +#define I2C_MCR_SB (0x1 << 11) /* Extended address */ +#define I2C_MCR_AM (0x3 << 12) /* Address type */ +#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */ +#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */ + +/* Status register (SR) */ +#define I2C_SR_OP (0x3 << 0) /* Operation */ +#define I2C_SR_STATUS (0x3 << 2) /* controller status */ +#define I2C_SR_CAUSE (0x7 << 4) /* Abort cause */ +#define I2C_SR_TYPE (0x3 << 7) /* Receive type */ +#define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */ + +/* Interrupt mask set/clear (IMSCR) bits */ +#define I2C_IT_TXFE (0x1 << 0) +#define I2C_IT_TXFNE (0x1 << 1) +#define I2C_IT_TXFF (0x1 << 2) +#define I2C_IT_TXFOVR (0x1 << 3) +#define I2C_IT_RXFE (0x1 << 4) +#define I2C_IT_RXFNF (0x1 << 5) +#define I2C_IT_RXFF (0x1 << 6) +#define I2C_IT_RFSR (0x1 << 16) +#define I2C_IT_RFSE (0x1 << 17) +#define I2C_IT_WTSR (0x1 << 18) +#define I2C_IT_MTD (0x1 << 19) +#define I2C_IT_STD (0x1 << 20) +#define I2C_IT_MAL (0x1 << 24) +#define I2C_IT_BERR (0x1 << 25) +#define I2C_IT_MTDWS (0x1 << 28) + +#define GEN_MASK(val, mask, sb) (((val) << (sb)) & (mask)) + +/* some bits in ICR are reserved */ +#define I2C_CLEAR_ALL_INTS 0x131f007f + +/* first three msb bits are reserved */ +#define IRQ_MASK(mask) (mask & 0x1fffffff) + +/* maximum threshold value */ +#define MAX_I2C_FIFO_THRESHOLD 15 + +enum i2c_status { + I2C_NOP, + I2C_ON_GOING, + I2C_OK, + I2C_ABORT +}; + +/* operation */ +enum i2c_operation { + I2C_NO_OPERATION = 0xff, + I2C_WRITE = 0x00, + I2C_READ = 0x01 +}; + +/* controller response timeout in ms */ +#define I2C_TIMEOUT_MS 500 + +/** + * struct i2c_nmk_client - client specific data + * @slave_adr: 7-bit slave address + * @count: no. bytes to be transfered + * @buffer: client data buffer + * @xfer_bytes: bytes transfered till now + * @operation: current I2C operation + */ +struct i2c_nmk_client { + unsigned short slave_adr; + unsigned long count; + unsigned char *buffer; + unsigned long xfer_bytes; + enum i2c_operation operation; +}; + +/** + * struct nmk_i2c_dev - private data structure of the controller + * @pdev: parent platform device + * @adap: corresponding I2C adapter + * @irq: interrupt line for the controller + * @virtbase: virtual io memory area + * @clk: hardware i2c block clock + * @cfg: machine provided controller configuration + * @cli: holder of client specific data + * @stop: stop condition + * @xfer_complete: acknowledge completion for a I2C message + * @result: controller propogated result + */ +struct nmk_i2c_dev { + struct platform_device *pdev; + struct i2c_adapter adap; + int irq; + void __iomem *virtbase; + struct clk *clk; + struct nmk_i2c_controller cfg; + struct i2c_nmk_client cli; + int stop; + struct completion xfer_complete; + int result; +}; + +/* controller's abort causes */ +static const char *abort_causes[] = { + "no ack received after address transmission", + "no ack received during data phase", + "ack received after xmission of master code", + "master lost arbitration", + "slave restarts", + "slave reset", + "overflow, maxsize is 2047 bytes", +}; + +static inline void i2c_set_bit(void __iomem *reg, u32 mask) +{ + writel(readl(reg) | mask, reg); +} + +static inline void i2c_clr_bit(void __iomem *reg, u32 mask) +{ + writel(readl(reg) & ~mask, reg); +} + +/** + * flush_i2c_fifo() - This function flushes the I2C FIFO + * @dev: private data of I2C Driver + * + * This function flushes the I2C Tx and Rx FIFOs. It returns + * 0 on successful flushing of FIFO + */ +static int flush_i2c_fifo(struct nmk_i2c_dev *dev) +{ +#define LOOP_ATTEMPTS 10 + int i; + unsigned long timeout; + + /* + * flush the transmit and receive FIFO. The flushing + * operation takes several cycles before to be completed. + * On the completion, the I2C internal logic clears these + * bits, until then no one must access Tx, Rx FIFO and + * should poll on these bits waiting for the completion. + */ + writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR); + + for (i = 0; i < LOOP_ATTEMPTS; i++) { + timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MS); + + while (!time_after(jiffies, timeout)) { + if ((readl(dev->virtbase + I2C_CR) & + (I2C_CR_FTX | I2C_CR_FRX)) == 0) + return 0; + } + } + + dev_err(&dev->pdev->dev, "flushing operation timed out " + "giving up after %d attempts", LOOP_ATTEMPTS); + + return -ETIMEDOUT; +} + +/** + * disable_all_interrupts() - Disable all interrupts of this I2c Bus + * @dev: private data of I2C Driver + */ +static void disable_all_interrupts(struct nmk_i2c_dev *dev) +{ + u32 mask = IRQ_MASK(0); + writel(mask, dev->virtbase + I2C_IMSCR); +} + +/** + * clear_all_interrupts() - Clear all interrupts of I2C Controller + * @dev: private data of I2C Driver + */ +static void clear_all_interrupts(struct nmk_i2c_dev *dev) +{ + u32 mask; + mask = IRQ_MASK(I2C_CLEAR_ALL_INTS); + writel(mask, dev->virtbase + I2C_ICR); +} + +/** + * init_hw() - initialize the I2C hardware + * @dev: private data of I2C Driver + */ +static int init_hw(struct nmk_i2c_dev *dev) +{ + int stat; + + stat = flush_i2c_fifo(dev); + if (stat) + return stat; + + /* disable the controller */ + i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + + disable_all_interrupts(dev); + + clear_all_interrupts(dev); + + dev->cli.operation = I2C_NO_OPERATION; + + return 0; +} + +/* enable peripheral, master mode operation */ +#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE) + +/** + * load_i2c_mcr_reg() - load the MCR register + * @dev: private data of controller + */ +static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev) +{ + u32 mcr = 0; + + /* 7-bit address transaction */ + mcr |= GEN_MASK(1, I2C_MCR_AM, 12); + mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1); + + /* start byte procedure not applied */ + mcr |= GEN_MASK(0, I2C_MCR_SB, 11); + + /* check the operation, master read/write? */ + if (dev->cli.operation == I2C_WRITE) + mcr |= GEN_MASK(I2C_WRITE, I2C_MCR_OP, 0); + else + mcr |= GEN_MASK(I2C_READ, I2C_MCR_OP, 0); + + /* stop or repeated start? */ + if (dev->stop) + mcr |= GEN_MASK(1, I2C_MCR_STOP, 14); + else + mcr &= ~(GEN_MASK(1, I2C_MCR_STOP, 14)); + + mcr |= GEN_MASK(dev->cli.count, I2C_MCR_LENGTH, 15); + + return mcr; +} + +/** + * setup_i2c_controller() - setup the controller + * @dev: private data of controller + */ +static void setup_i2c_controller(struct nmk_i2c_dev *dev) +{ + u32 brcr1, brcr2; + u32 i2c_clk, div; + + writel(0x0, dev->virtbase + I2C_CR); + writel(0x0, dev->virtbase + I2C_HSMCR); + writel(0x0, dev->virtbase + I2C_TFTR); + writel(0x0, dev->virtbase + I2C_RFTR); + writel(0x0, dev->virtbase + I2C_DMAR); + + /* + * set the slsu: + * + * slsu defines the data setup time after SCL clock + * stretching in terms of i2c clk cycles. The + * needed setup time for the three modes are 250ns, + * 100ns, 10ns repectively thus leading to the values + * of 14, 6, 2 for a 48 MHz i2c clk. + */ + writel(dev->cfg.slsu << 16, dev->virtbase + I2C_SCR); + + i2c_clk = clk_get_rate(dev->clk); + + /* fallback to std. mode if machine has not provided it */ + if (dev->cfg.clk_freq == 0) + dev->cfg.clk_freq = 100000; + + /* + * The spec says, in case of std. mode the divider is + * 2 whereas it is 3 for fast and fastplus mode of + * operation. TODO - high speed support. + */ + div = (dev->cfg.clk_freq > 100000) ? 3 : 2; + + /* + * generate the mask for baud rate counters. The controller + * has two baud rate counters. One is used for High speed + * operation, and the other is for std, fast mode, fast mode + * plus operation. Currently we do not supprt high speed mode + * so set brcr1 to 0. + */ + brcr1 = 0 << 16; + brcr2 = (i2c_clk/(dev->cfg.clk_freq * div)) & 0xffff; + + /* set the baud rate counter register */ + writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR); + + /* + * set the speed mode. Currently we support + * only standard and fast mode of operation + * TODO - support for fast mode plus (upto 1Mb/s) + * and high speed (up to 3.4 Mb/s) + */ + if (dev->cfg.sm > I2C_FREQ_MODE_FAST) { + dev_err(&dev->pdev->dev, "do not support this mode " + "defaulting to std. mode\n"); + brcr2 = i2c_clk/(100000 * 2) & 0xffff; + writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR); + writel(I2C_FREQ_MODE_STANDARD << 4, + dev->virtbase + I2C_CR); + } + writel(dev->cfg.sm << 4, dev->virtbase + I2C_CR); + + /* set the Tx and Rx FIFO threshold */ + writel(dev->cfg.tft, dev->virtbase + I2C_TFTR); + writel(dev->cfg.rft, dev->virtbase + I2C_RFTR); +} + +/** + * read_i2c() - Read from I2C client device + * @dev: private data of I2C Driver + * + * This function reads from i2c client device when controller is in + * master mode. There is a completion timeout. If there is no transfer + * before timeout error is returned. + */ +static int read_i2c(struct nmk_i2c_dev *dev) +{ + u32 status = 0; + u32 mcr; + u32 irq_mask = 0; + int timeout; + + mcr = load_i2c_mcr_reg(dev); + writel(mcr, dev->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + dev->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE); + + init_completion(&dev->xfer_complete); + + /* enable interrupts by setting the mask */ + irq_mask = (I2C_IT_RXFNF | I2C_IT_RXFF | + I2C_IT_MAL | I2C_IT_BERR); + + if (dev->stop) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask); + + writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask, + dev->virtbase + I2C_IMSCR); + + timeout = wait_for_completion_interruptible_timeout( + &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS)); + + if (timeout < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout" + "returned %d waiting for event\n", timeout); + status = timeout; + } + + if (timeout == 0) { + /* controler has timedout, re-init the h/w */ + dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n"); + (void) init_hw(dev); + status = -ETIMEDOUT; + } + + return status; +} + +/** + * write_i2c() - Write data to I2C client. + * @dev: private data of I2C Driver + * + * This function writes data to I2C client + */ +static int write_i2c(struct nmk_i2c_dev *dev) +{ + u32 status = 0; + u32 mcr; + u32 irq_mask = 0; + int timeout; + + mcr = load_i2c_mcr_reg(dev); + + writel(mcr, dev->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + dev->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + + init_completion(&dev->xfer_complete); + + /* enable interrupts by settings the masks */ + irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR | + I2C_IT_MAL | I2C_IT_BERR); + + /* + * check if we want to transfer a single or multiple bytes, if so + * set the MTDWS bit (Master Transaction Done Without Stop) + * to start repeated start operation + */ + if (dev->stop) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask); + + writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask, + dev->virtbase + I2C_IMSCR); + + timeout = wait_for_completion_interruptible_timeout( + &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS)); + + if (timeout < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout" + "returned %d waiting for event\n", timeout); + status = timeout; + } + + if (timeout == 0) { + /* controler has timedout, re-init the h/w */ + dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n"); + (void) init_hw(dev); + status = -ETIMEDOUT; + } + + return status; +} + +/** + * nmk_i2c_xfer() - I2C transfer function used by kernel framework + * @i2c_adap - Adapter pointer to the controller + * @msgs[] - Pointer to data to be written. + * @num_msgs - Number of messages to be executed + * + * This is the function called by the generic kernel i2c_transfer() + * or i2c_smbus...() API calls. Note that this code is protected by the + * semaphore set in the kernel i2c_transfer() function. + * + * NOTE: + * READ TRANSFER : We impose a restriction of the first message to be the + * index message for any read transaction. + * - a no index is coded as '0', + * - 2byte big endian index is coded as '3' + * !!! msg[0].buf holds the actual index. + * This is compatible with generic messages of smbus emulator + * that send a one byte index. + * eg. a I2C transation to read 2 bytes from index 0 + * idx = 0; + * msg[0].addr = client->addr; + * msg[0].flags = 0x0; + * msg[0].len = 1; + * msg[0].buf = &idx; + * + * msg[1].addr = client->addr; + * msg[1].flags = I2C_M_RD; + * msg[1].len = 2; + * msg[1].buf = rd_buff + * i2c_transfer(adap, msg, 2); + * + * WRITE TRANSFER : The I2C standard interface interprets all data as payload. + * If you want to emulate an SMBUS write transaction put the + * index as first byte(or first and second) in the payload. + * eg. a I2C transation to write 2 bytes from index 1 + * wr_buff[0] = 0x1; + * wr_buff[1] = 0x23; + * wr_buff[2] = 0x46; + * msg[0].flags = 0x0; + * msg[0].len = 3; + * msg[0].buf = wr_buff; + * i2c_transfer(adap, msg, 1); + * + * To read or write a block of data (multiple bytes) using SMBUS emulation + * please use the i2c_smbus_read_i2c_block_data() + * or i2c_smbus_write_i2c_block_data() API + */ +static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num_msgs) +{ + int status; + int i; + u32 cause; + struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); + + status = init_hw(dev); + if (status) + return status; + + /* setup the i2c controller */ + setup_i2c_controller(dev); + + for (i = 0; i < num_msgs; i++) { + if (unlikely(msgs[i].flags & I2C_M_TEN)) { + dev_err(&dev->pdev->dev, "10 bit addressing" + "not supported\n"); + return -EINVAL; + } + dev->cli.slave_adr = msgs[i].addr; + dev->cli.buffer = msgs[i].buf; + dev->cli.count = msgs[i].len; + dev->stop = (i < (num_msgs - 1)) ? 0 : 1; + dev->result = 0; + + if (msgs[i].flags & I2C_M_RD) { + /* it is a read operation */ + dev->cli.operation = I2C_READ; + status = read_i2c(dev); + } else { + /* write operation */ + dev->cli.operation = I2C_WRITE; + status = write_i2c(dev); + } + if (status || (dev->result)) { + /* get the abort cause */ + cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7; + dev_err(&dev->pdev->dev, "error during I2C" + "message xfer: %d\n", cause); + dev_err(&dev->pdev->dev, "%s\n", + cause >= ARRAY_SIZE(abort_causes) + ? "unknown reason" : abort_causes[cause]); + return status; + } + mdelay(1); + } + /* return the no. messages processed */ + if (status) + return status; + else + return num_msgs; +} + +/** + * disable_interrupts() - disable the interrupts + * @dev: private data of controller + */ +static int disable_interrupts(struct nmk_i2c_dev *dev, u32 irq) +{ + irq = IRQ_MASK(irq); + writel(readl(dev->virtbase + I2C_IMSCR) & ~(I2C_CLEAR_ALL_INTS & irq), + dev->virtbase + I2C_IMSCR); + return 0; +} + +/** + * i2c_irq_handler() - interrupt routine + * @irq: interrupt number + * @arg: data passed to the handler + * |