/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #ifdef CONFIG_ARCH_SCX35 //#include #include #include #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 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, "); MODULE_LICENSE("GPL");