diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-sprd.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-sprd.c | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c new file mode 100644 index 00000000..d80a04ba --- /dev/null +++ b/drivers/i2c/busses/i2c-sprd.c @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_i2c.h> + +#include <asm/io.h> + +//#include <mach/globalregs.h> + +#ifdef CONFIG_ARCH_SCX35 +//#include <mach/hardware.h> +#include <soc/sprd/sci.h> +#include <soc/sprd/sci_glb_regs.h> +#endif + +#define SPRD_I2C_CTL_ID (6) + +/*Note: The defined below are for tiger and later chipset. */ +/*If we don't use cmd buffer function, the define work well for the old chipset*/ +/*register offset*/ +#define I2C_CTL 0x0000 +#define I2C_CMD 0x0004 +#define I2C_CLKD0 0x0008 +#define I2C_CLKD1 0x000C +#define I2C_RST 0x0010 +#define I2C_CMD_BUF 0x0014 +#define I2C_CMD_BUF_CTL 0x0018 +#define I2C_STA_STO_DVD 0X001C + +/*The corresponding bit of I2C_CTL register*/ +#define I2C_CTL_INT (1 << 0) /* I2c interrupt */ +#define I2C_CTL_ACK (1 << 1) /* I2c received ack value */ +#define I2C_CTL_BUSY (1 << 2) /* I2c data line value */ +#define I2C_CTL_IE (1 << 3) /* I2c interrupt enable */ +#define I2C_CTL_EN (1 << 4) /* I2c module enable */ +#define I2C_CTL_SCL_LINE (1 << 5) /*scl line signal */ +#define I2C_CTL_SDA_LINE (1 << 6) /* sda line signal */ +#define I2C_CTL_NOACK_INT_EN (1 << 7) /* no ack int enable */ +#define I2C_CTL_NOACK_INT_STS (1 << 8) /* no ack int status */ +#define I2C_CTL_NOACK_INT_CLR (1 << 9) /* no ack int clear */ + +#if defined(CONFIG_I2C_SPRD_R6P0_DUTY) +#define I2C_CTL_DUTY (1 << 11) /* I2c DUTY*/ +#endif + +/*The corresponding bit of I2C_CMD register*/ +#define I2C_CMD_INT_ACK (1 << 0) /* I2c interrupt clear bit */ +#define I2C_CMD_TX_ACK (1 << 1) /* I2c transmit ack that need to be send */ +#define I2C_CMD_WRITE (1 << 2) /* I2c write command */ +#define I2C_CMD_READ (1 << 3) /* I2c read command */ +#define I2C_CMD_STOP (1 << 4) /* I2c stop command */ +#define I2C_CMD_START (1 << 5) /* I2c start command */ +#define I2C_CMD_ACK (1 << 6) /* I2c received ack value */ +#define I2C_CMD_BUSY (1 << 7) /* I2c busy in exec commands */ +#define I2C_CMD_DATA 0xFF00 /* I2c data received or data need to be transmitted */ + +/*The corresponding bit of I2C_RST register*/ +#define I2C_RST_RST (1 << 0) /* I2c reset bit */ + +/*The corresponding bit of I2C_CMD_BUF_CTL register*/ +#define I2C_CTL_CMDBUF_EN (1 << 0) /* Enable the cmd buffer mode */ +#define I2C_CTL_CMDBUF_EXEC (1 << 1) /* Start to exec the cmd in the cmd buffer */ + +#ifdef CONFIG_I2C_RESUME_EARLY +#include <linux/syscore_ops.h> +static struct platform_device *pdev_chip_i2c[SPRD_I2C_CTL_ID]; +#endif + +/* i2c data structure*/ +struct sprd_i2c { + struct i2c_msg *msg; + struct i2c_adapter adap; + void __iomem *membase; + struct clk *clk; + int irq; +}; + +struct sprd_platform_i2c { + unsigned int normal_freq; /* normal bus frequency */ + unsigned int fast_freq; /* fast frequency for the bus */ + unsigned int min_freq; /* min frequency for the bus */ +}; + +static struct sprd_platform_i2c sprd_platform_i2c_default = { + .normal_freq = 100 * 1000, + .fast_freq = 400 * 1000, + .min_freq = 10 * 1000, +}; + +static struct sprd_i2c *sprd_i2c_ctl_id[SPRD_I2C_CTL_ID]; +static unsigned int sprd_version_num = 0x0600; /*I2c have version number after r6p0*/ + +static inline struct sprd_platform_i2c *sprd_i2c_get_platformdata(struct device + *dev) +{ + if (dev != NULL && dev->platform_data != NULL) + return (struct sprd_platform_i2c *)dev->platform_data; + + return &sprd_platform_i2c_default; +} + +static inline int +sprd_i2c_poll_ctl_status(struct sprd_i2c *pi2c, unsigned long bit) +{ + int loop_cntr = 5000; + + do { + udelay(1); + } + while (!(__raw_readl(pi2c->membase + I2C_CTL) & bit) + && (--loop_cntr > 0)); + + if (loop_cntr > 0) + return 1; + else + return -1; +} + +static inline int +sprd_i2c_poll_cmd_status(struct sprd_i2c *pi2c, unsigned long bit) +{ + int loop_cntr = 5000; + + do { + udelay(1); + } + while ((__raw_readl(pi2c->membase + I2C_CMD) & bit) + && (--loop_cntr > 0)); + + if (loop_cntr > 0) + return 1; + else + return -1; +} + +static inline int sprd_i2c_wait_int(struct sprd_i2c *pi2c) +{ + return sprd_i2c_poll_ctl_status(pi2c, I2C_CTL_INT); +} + +static inline int sprd_i2c_wait_busy(struct sprd_i2c *pi2c) +{ + return sprd_i2c_poll_cmd_status(pi2c, I2C_CMD_BUSY); +} + +static inline int sprd_i2c_wait_ack(struct sprd_i2c *pi2c) +{ + return sprd_i2c_poll_cmd_status(pi2c, I2C_CMD_ACK); +} + +static inline void sprd_i2c_clear_int(struct sprd_i2c *pi2c) +{ + unsigned int cmd = 0; + + sprd_i2c_wait_busy(pi2c); + + cmd = (__raw_readl(pi2c->membase + I2C_CMD) & 0xff00) | I2C_CMD_INT_ACK; + __raw_writel(cmd, pi2c->membase + I2C_CMD); +} + +static inline void dump_i2c_reg(struct sprd_i2c *pi2c) +{ + printk(KERN_ERR ": ======dump i2c-%d reg=======\n", pi2c->adap.nr); + printk(KERN_ERR ": I2C_CTRL:0x%x\n",__raw_readl(pi2c->membase + I2C_CTL)); + printk(KERN_ERR ": I2C_CMD:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD)); + printk(KERN_ERR ": I2C_DVD0:0x%x\n",__raw_readl(pi2c->membase + I2C_CLKD0)); + printk(KERN_ERR ": I2C_DVD1:0x%x\n",__raw_readl(pi2c->membase + I2C_CLKD1)); + printk(KERN_ERR ": I2C_RST:0x%x\n",__raw_readl(pi2c->membase + I2C_RST)); + printk(KERN_ERR ": I2C_CMD_BUF:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD_BUF)); + printk(KERN_ERR ": I2C_CMD_BUF_CTL:0x%x\n",__raw_readl(pi2c->membase + I2C_CMD_BUF_CTL)); +} + +static inline int sprd_wait_trx_done(struct sprd_i2c *pi2c) +{ + int rc; + + rc = sprd_i2c_wait_int(pi2c); + if (rc < 0) { + dev_err(&pi2c->adap.dev, "%s() err! rc=%d\n", __func__, rc); + dump_i2c_reg(pi2c); + return rc; + } + + sprd_i2c_clear_int(pi2c); + + return sprd_i2c_wait_ack(pi2c); +} + +static int +sprd_i2c_write_byte(struct sprd_i2c *pi2c, char byte, int stop, int is_last_msg) +{ + int rc = 0; + int cmd; + + if (stop && is_last_msg) { + cmd = (byte << 8) | I2C_CMD_WRITE | I2C_CMD_STOP; + } else { + cmd = (byte << 8) | I2C_CMD_WRITE; + } + + dev_dbg(&pi2c->adap.dev, "%s() cmd=%x\n", __func__, cmd); + __raw_writel(cmd, pi2c->membase + I2C_CMD); + + rc = sprd_wait_trx_done(pi2c); + return rc; +} + +static int sprd_i2c_read_byte(struct sprd_i2c *pi2c, char *byte, int stop) +{ + int rc = 0; + int cmd; + + if (stop) { + cmd = I2C_CMD_READ | I2C_CMD_STOP | I2C_CMD_TX_ACK; + } else { + cmd = I2C_CMD_READ; + } + __raw_writel(cmd, pi2c->membase + I2C_CMD); + dev_dbg(&pi2c->adap.dev, "%s() cmd=%x\n", __func__, cmd); + + rc = sprd_wait_trx_done(pi2c); + if (rc < 0) { + dev_err(&pi2c->adap.dev, "%s() err! rc=%d\n", __func__, rc); + return rc; + } + + *byte = (unsigned char)(__raw_readl(pi2c->membase + I2C_CMD) >> 8); + dev_dbg(&pi2c->adap.dev, "%s() byte=%x, cmd reg=%x\n", __func__, *byte, + __raw_readl(pi2c->membase + I2C_CMD)); + + return rc; +} + +static int +sprd_i2c_writebytes(struct sprd_i2c *pi2c, const char *buf, int count, + int is_last_msg) +{ + int ii; + int rc = 0; + + for (ii = 0; rc >= 0 && ii != count; ++ii) + rc = sprd_i2c_write_byte(pi2c, buf[ii], ii == count - 1, + is_last_msg); + return rc; +} + +static int sprd_i2c_readbytes(struct sprd_i2c *pi2c, char *buf, int count) +{ + int ii; + int rc = 0; + + for (ii = 0; rc >= 0 && ii != count; ++ii) + rc = sprd_i2c_read_byte(pi2c, &buf[ii], ii == count - 1); + + return rc; +} + +static int sprd_i2c_send_target_addr(struct sprd_i2c *pi2c, struct i2c_msg *msg) +{ + int rc = 0; + int cmd = 0; + int cmd2 = 0; + int tmp = 0; + + if (msg->flags & I2C_M_TEN) { + cmd = 0xf0 | (((msg->addr >> 8) & 0x03) << 1); + cmd2 = msg->addr & 0xff; + } else { + cmd = (msg->addr & 0x7f) << 1; + } + + if (msg->flags & I2C_M_RD) + cmd |= 1; + + tmp = __raw_readl(pi2c->membase + I2C_CTL); +// dev_info (&pi2c->adap.dev, "%s() ctl=%x\n", __func__, tmp); + + tmp = __raw_readl(pi2c->membase + I2C_CTL); + __raw_writel(tmp | I2C_CTL_EN | I2C_CTL_IE, pi2c->membase + I2C_CTL); + + tmp = __raw_readl(pi2c->membase + I2C_CTL); +// dev_info (&pi2c->adap.dev, "%s() ctl=%x\n", __func__, tmp); + + cmd = (cmd << 8) | I2C_CMD_START | I2C_CMD_WRITE; +// dev_info (&pi2c->adap.dev, "%s() cmd=%x\n", __func__, cmd); + __raw_writel(cmd, pi2c->membase + I2C_CMD); + + rc = sprd_wait_trx_done(pi2c); + if (rc < 0) { +// dev_err (&pi2c->adap.dev, "%s() rc=%d\n", __func__, rc); + return rc; + } + + if ((msg->flags & I2C_M_TEN) && (!(msg->flags & I2C_M_RD))) { + cmd2 = (cmd2 << 8) | I2C_CMD_WRITE; +// dev_info (&pi2c->adap.dev, "%s() cmd2=%x\n", __func__, cmd2); + __raw_writel(cmd2, pi2c->membase + I2C_CMD); + + rc = sprd_wait_trx_done(pi2c); + if (rc < 0) { +// dev_err (&pi2c->adap.dev, "%s() rc=%d\n", __func__, rc); + return rc; + } + } + + return rc; +} + +static int +sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg, + int is_last_msg) +{ + struct sprd_i2c *pi2c = i2c_adap->algo_data; + int rc; + + dev_dbg(&i2c_adap->dev, "%s() flag=%x, adr=%x, len=%d\n", __func__, + pmsg->flags, pmsg->addr, pmsg->len); + + rc = sprd_i2c_send_target_addr(pi2c, pmsg); + if (rc < 0) { + dev_err(&i2c_adap->dev, "%s() rc=%d\n", __func__, rc); + dump_i2c_reg(pi2c); + return rc; + } + + if ((pmsg->flags & I2C_M_RD)) { + return sprd_i2c_readbytes(pi2c, pmsg->buf, pmsg->len); + } else { + return sprd_i2c_writebytes(pi2c, pmsg->buf, pmsg->len, + is_last_msg); + } +} + +static int +sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + int num) +{ + int im = 0; + int ret = 0; + struct sprd_i2c *pi2c = i2c_adap->algo_data; + clk_enable(pi2c->clk); + + dev_dbg(&i2c_adap->dev, "%s() msg num=%d\n", __func__, num); + + for (im = 0; ret >= 0 && im != num; im++) { + dev_dbg(&i2c_adap->dev, "%s() msg im=%d\n", __func__, im); + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im], im == num - 1); + } + clk_disable(pi2c->clk); + + return (ret >= 0)? im : -1; +} + +static u32 sprd_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sprd_i2c_algo = { + .master_xfer = sprd_i2c_master_xfer, + .functionality = sprd_i2c_func, +}; + +static void sprd_i2c_set_clk(struct sprd_i2c *pi2c, unsigned int freq) +{ + unsigned int apb_clk; + unsigned int i2c_div; + + apb_clk = 26000000; + #ifdef CONFIG_ARCH_SCX15 + i2c_div = apb_clk / (4 * freq) - 3; + #else + i2c_div = apb_clk / (4 * freq) - 1; + #endif + + #if defined(CONFIG_I2C_SPRD_R6P0_DUTY) + if(freq == 400000){ + __raw_writel(0x0012000c, pi2c->membase + I2C_CLKD0); + } + else + __raw_writel(((i2c_div & 0xffff)<<16|(i2c_div & 0xffff)), pi2c->membase + I2C_CLKD0); + #else + __raw_writel(i2c_div & 0xffff, pi2c->membase + I2C_CLKD0); + #endif + + /*fixed 400k start time issue after r7p0*/ + if ((sprd_version_num >>8) >= 0x07) { + __raw_writel(0X75, pi2c->membase + I2C_STA_STO_DVD); + if(freq == 400000) + __raw_writel(0X10, pi2c->membase + I2C_STA_STO_DVD); + } + + + #if defined(CONFIG_I2C_SPRD_R6P0_DUTY) + __raw_writel((i2c_div >> 16|(i2c_div& 0xffff0000)), pi2c->membase + I2C_CLKD1); + #else + __raw_writel(i2c_div >> 16, pi2c->membase + I2C_CLKD1); + #endif +} + +void sprd_i2c_ctl_chg_clk(unsigned int id_nr, unsigned int freq) +{ + unsigned int tmp; + clk_enable(sprd_i2c_ctl_id[id_nr]->clk); + + tmp = __raw_readl(sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + __raw_writel(tmp & (~I2C_CTL_EN), + sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + tmp = __raw_readl(sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + + sprd_i2c_set_clk(sprd_i2c_ctl_id[id_nr], freq); + + tmp = __raw_readl(sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + __raw_writel(tmp | I2C_CTL_EN, + sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + tmp = __raw_readl(sprd_i2c_ctl_id[id_nr]->membase + I2C_CTL); + clk_disable(sprd_i2c_ctl_id[id_nr]->clk); + +} + +EXPORT_SYMBOL_GPL(sprd_i2c_ctl_chg_clk); + +static int sprd_i2c_clk_init(struct sprd_i2c *pi2c) +{ +#if defined(CONFIG_ARCH_SCX35) + char buf[256] = { 0 }; + if (unlikely(pi2c->adap.nr >= 5)) /* dolphin less than 5 and shark's i2c device that large than 5 is named clk_i2c' */ + strcpy(buf, "clk_i2c"); + else + sprintf(buf, "clk_i2c%d", pi2c->adap.nr); + dev_info(&pi2c->adap.dev, "%s buf=%s", __func__, buf); + + pi2c->clk = clk_get(&pi2c->adap.dev, buf); + if (IS_ERR(pi2c->clk)) { + return -ENODEV; + } + +#elif defined(CONFIG_ARCH_SC8825) + /*enable i2c clock */ + sprd_greg_set_bits(REG_TYPE_GLOBAL, (0x07 << 29) | BIT(4), GR_GEN0); + /*reset i2c module */ + sprd_greg_set_bits(REG_TYPE_GLOBAL, (0x07 << 2) | 0x01, GR_SOFT_RST); + sprd_greg_clear_bits(REG_TYPE_GLOBAL, (0x07 << 2) | 0x01, GR_SOFT_RST); + /*flush cmd buffer */ + __raw_writel(I2C_RST_RST, pi2c->membase + I2C_RST); + __raw_writel(0, pi2c->membase + I2C_RST); +#endif +return 0; + +} + +static void sprd_i2c_enable(struct sprd_i2c *pi2c) +{ + unsigned int tmp; + struct sprd_platform_i2c *pdata; + + tmp = __raw_readl(pi2c->membase + I2C_CTL); + __raw_writel(tmp & ~I2C_CTL_EN, pi2c->membase + I2C_CTL); + tmp = __raw_readl(pi2c->membase + I2C_CTL); + __raw_writel(tmp & ~I2C_CTL_IE, pi2c->membase + I2C_CTL); + tmp = __raw_readl(pi2c->membase + I2C_CTL); + __raw_writel(tmp & ~I2C_CTL_CMDBUF_EN, pi2c->membase + I2C_CTL); + + pdata = sprd_i2c_get_platformdata(pi2c->adap.dev.parent); + dev_dbg(&pi2c->adap.dev, "%s() freq=%d\n", __func__, + pdata->normal_freq); + sprd_i2c_set_clk(pi2c, pdata->normal_freq); + + tmp = __raw_readl(pi2c->membase + I2C_CTL); + sprd_version_num = tmp >> 16; + #if defined(CONFIG_I2C_SPRD_R6P0_DUTY) + __raw_writel(tmp | I2C_CTL_EN | I2C_CTL_IE|I2C_CTL_DUTY, pi2c->membase + I2C_CTL); + #else + __raw_writel(tmp | I2C_CTL_EN | I2C_CTL_IE, pi2c->membase + I2C_CTL); + #endif + + __raw_writel(I2C_CMD_INT_ACK, pi2c->membase + I2C_CMD); + +} + +static int sprd_i2c_probe(struct platform_device *pdev) +{ + struct sprd_i2c *pi2c; + struct resource *res; + int ret; + struct device_node *np = pdev->dev.of_node; + + if (np) + pdev->id = of_alias_get_id(np, "i2c"); + + pi2c = kzalloc(sizeof(struct sprd_i2c), GFP_KERNEL); + if (!pi2c) { + ret = -ENOMEM; + goto out; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto free_adapter; + } +#if 0 + if (!request_mem_region(res->start, resource_size(res), "sc8810-i2c")) { + printk("I2C:request_mem_region failed!\n"); + ret = -EBUSY; + goto free_adapter; + } +#endif + + i2c_set_adapdata(&pi2c->adap, pi2c); + snprintf(pi2c->adap.name, sizeof(pi2c->adap.name), "%s", "sprd-i2c"); + pi2c->adap.owner = THIS_MODULE; + pi2c->adap.retries = 3; + pi2c->adap.algo = &sprd_i2c_algo; + pi2c->adap.algo_data = pi2c; + pi2c->adap.dev.parent = &pdev->dev; + pi2c->adap.nr = pdev->id; + pi2c->membase = (void *)ioremap_nocache(res->start, + res->end - res->start); + if (!pi2c->membase) + panic("ioremap failed!\n"); + pi2c->adap.dev.of_node = pdev->dev.of_node; + dev_info(&pdev->dev, "%s() id=%d, base=%p \n", __func__, pi2c->adap.nr, + pi2c->membase); + ret = sprd_i2c_clk_init(pi2c); + if (ret) { + dev_err(&pdev->dev, "get src clk failed\n"); + goto release_region; + } + + clk_prepare_enable(pi2c->clk); + sprd_i2c_enable(pi2c); + + clk_disable(pi2c->clk); + + + ret = i2c_add_numbered_adapter(&pi2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "add_adapter failed!\n"); + clk_unprepare(pi2c->clk); + goto release_region; + } + + sprd_i2c_ctl_id[pdev->id] = pi2c; + platform_set_drvdata(pdev, pi2c); + +#ifdef CONFIG_I2C_RESUME_EARLY + pdev_chip_i2c[pdev->id] = pdev; +#endif + of_i2c_register_devices(&pi2c->adap); + + return 0; + +release_region: + //release_mem_region(res->start, resource_size(res)); +free_adapter: + kfree(pi2c); +out: + return ret; +} + +static int sprd_i2c_remove(struct platform_device *pdev) +{ + struct sprd_i2c *pi2c = platform_get_drvdata(pdev); + //struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + i2c_del_adapter(&pi2c->adap); + kfree(pi2c); + + //release_mem_region(res->start, resource_size(res)); + clk_unprepare(pi2c->clk); + platform_set_drvdata(pdev, NULL); + +#ifdef CONFIG_I2C_RESUME_EARLY + pdev_chip_i2c[pdev->id] = NULL; +#endif + + return 0; +} + +#if defined (CONFIG_PM) && defined(CONFIG_ARCH_SCX35) +static int i2c_controller_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int i2c_controller_resume(struct platform_device *pdev) +{ + struct sprd_i2c *pi2c = platform_get_drvdata(pdev); + + clk_enable(pi2c->clk); + sprd_i2c_enable(pi2c); + clk_disable(pi2c->clk); + return 0; +} + +#ifdef CONFIG_I2C_RESUME_EARLY +static int i2c_controller_suspend_late(void) +{ + int i; + pm_message_t state = { + .event = 0 + }; + + for (i = 0; i < ARRAY_SIZE(sprd_i2c_ctl_id); i++) { + if (pdev_chip_i2c[i] == NULL) + continue; + i2c_controller_suspend(pdev_chip_i2c[i], state); + } + + return 0; +} + +static void i2c_controller_resume_early(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sprd_i2c_ctl_id); i++) { + if (pdev_chip_i2c[i] == NULL) + continue; + i2c_controller_resume(pdev_chip_i2c[i]); + } +} + +static struct syscore_ops sprd_i2c_syscore_ops = { + .suspend = i2c_controller_suspend_late, + .resume = i2c_controller_resume_early, +}; + +static int __init sprd_i2c_syscore_init(void) +{ + register_syscore_ops(&sprd_i2c_syscore_ops); + return 0; +} +subsys_initcall(sprd_i2c_syscore_init); +#endif /* CONFIG_I2C_RESUME_EARLY */ +#else +#define i2c_controller_suspend NULL +#define i2c_controller_resume NULL +#endif + +static struct of_device_id sprd_i2c_of_match[] = { + { .compatible = "sprd,i2c", }, + { } +}; + +static struct platform_driver sprd_i2c_driver = { + .probe = sprd_i2c_probe, + .remove = sprd_i2c_remove, + .driver = { + .owner = THIS_MODULE, + .name = "sprd-i2c", + .of_match_table = of_match_ptr(sprd_i2c_of_match), + }, +#ifndef CONFIG_I2C_RESUME_EARLY + .suspend = i2c_controller_suspend, + .resume = i2c_controller_resume, +#endif +}; + +static int __init sprd_i2c_init(void) +{ + return platform_driver_register(&sprd_i2c_driver); +} +#if defined(CONFIG_REGULATOR_EXT_DCDC) +arch_initcall_sync(sprd_i2c_init); +#else +subsys_initcall(sprd_i2c_init); +#endif + +static void __exit sprd_i2c_exit(void) +{ + platform_driver_unregister(&sprd_i2c_driver); +} + +module_exit(sprd_i2c_exit); + +MODULE_DESCRIPTION("sprd iic algorithm and driver"); +MODULE_AUTHOR("hao.liu, <hao.liu@spreadtrum.com>"); +MODULE_LICENSE("GPL"); |
