/* drivers/i2c/busses/i2c-msm.c * * Copyright (C) 2007 Google, 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 #define DEBUG 0 enum { I2C_WRITE_DATA = 0x00, I2C_CLK_CTL = 0x04, I2C_STATUS = 0x08, I2C_READ_DATA = 0x0c, I2C_INTERFACE_SELECT = 0x10, I2C_WRITE_DATA_DATA_BYTE = 0xff, I2C_WRITE_DATA_ADDR_BYTE = 1U << 8, I2C_WRITE_DATA_LAST_BYTE = 1U << 9, I2C_CLK_CTL_FS_DIVIDER_VALUE = 0xff, I2C_CLK_CTL_HS_DIVIDER_VALUE = 7U << 8, I2C_STATUS_WR_BUFFER_FULL = 1U << 0, I2C_STATUS_RD_BUFFER_FULL = 1U << 1, I2C_STATUS_BUS_ERROR = 1U << 2, I2C_STATUS_PACKET_NACKED = 1U << 3, I2C_STATUS_ARB_LOST = 1U << 4, I2C_STATUS_INVALID_WRITE = 1U << 5, I2C_STATUS_FAILED = 3U << 6, I2C_STATUS_BUS_ACTIVE = 1U << 8, I2C_STATUS_BUS_MASTER = 1U << 9, I2C_STATUS_ERROR_MASK = 0xfc, I2C_INTERFACE_SELECT_INTF_SELECT = 1U << 0, I2C_INTERFACE_SELECT_SCL = 1U << 8, I2C_INTERFACE_SELECT_SDA = 1U << 9, }; struct msm_i2c_dev { struct device *dev; void __iomem *base; /* virtual */ int irq; struct clk *clk; struct i2c_adapter adapter; spinlock_t lock; struct i2c_msg *msg; int rem; int pos; int cnt; int ret; bool need_flush; int flush_cnt; void *complete; struct wake_lock wakelock; bool is_suspended; int clk_drv_str; int dat_drv_str; int skip_recover; }; #if DEBUG static void dump_status(uint32_t status) { printk("STATUS (0x%.8x): ", status); if (status & I2C_STATUS_BUS_MASTER) printk("MST "); if (status & I2C_STATUS_BUS_ACTIVE) printk("ACT "); if (status & I2C_STATUS_INVALID_WRITE) printk("INV_WR "); if (status & I2C_STATUS_ARB_LOST) printk("ARB_LST "); if (status & I2C_STATUS_PACKET_NACKED) printk("NAK "); if (status & I2C_STATUS_BUS_ERROR) printk("BUS_ERR "); if (status & I2C_STATUS_RD_BUFFER_FULL) printk("RD_FULL "); if (status & I2C_STATUS_WR_BUFFER_FULL) printk("WR_FULL "); if (status & I2C_STATUS_FAILED) printk("FAIL 0x%x", (status & I2C_STATUS_FAILED)); printk("\n"); } #endif static void msm_i2c_write_delay(struct msm_i2c_dev *dev) { /* If scl is still high we have >4us (for 100kbps) to write the data * register before we risk hitting a bug where the controller releases * scl to soon after driving sda low. Writing the data after the * scheduled release time for scl also avoids the bug. */ if (readl(dev->base + I2C_INTERFACE_SELECT) & I2C_INTERFACE_SELECT_SCL) return; udelay(6); } static bool msm_i2c_fill_write_buffer(struct msm_i2c_dev *dev) { uint16_t val; if (dev->pos < 0) { val = I2C_WRITE_DATA_ADDR_BYTE | dev->msg->addr << 1; if (dev->msg->flags & I2C_M_RD) val |= 1; if (dev->rem == 1 && dev->msg->len == 0) val |= I2C_WRITE_DATA_LAST_BYTE; msm_i2c_write_delay(dev); writel(val, dev->base + I2C_WRITE_DATA); dev->pos++; return true; } if (dev->msg->flags & I2C_M_RD) return false; if (!dev->cnt) return false; /* Ready to take a byte */ val = dev->msg->buf[dev->pos]; if (dev->cnt == 1 && dev->rem == 1) val |= I2C_WRITE_DATA_LAST_BYTE; msm_i2c_write_delay(dev); writel(val, dev->base + I2C_WRITE_DATA); dev->pos++; dev->cnt--; return true; } static void msm_i2c_read_buffer(struct msm_i2c_dev *dev) { /* * Theres something in the FIFO. * Are we expecting data or flush crap? */ if ((dev->msg->flags & I2C_M_RD) && dev->pos >= 0 && dev->cnt) { switch (dev->cnt) { case 1: if (dev->pos != 0) break; dev->need_flush = true; /* fall-trough */ case 2: writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); } dev->msg->buf[dev->pos] = readl(dev->base + I2C_READ_DATA); dev->cnt--; dev->pos++; } else { /* FLUSH */ if (dev->flush_cnt & 1) { /* * Stop requests are sometimes ignored, but writing * more than one request generates a write error. */ writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); } readl(dev->base + I2C_READ_DATA); if (dev->need_flush) dev->need_flush = false; else dev->flush_cnt++; } } static void msm_i2c_interrupt_locked(struct msm_i2c_dev *dev) { uint32_t status = readl(dev->base + I2C_STATUS); bool not_done = true; #if DEBUG dump_status(status); #endif if (!dev->msg) { dev_err(dev->dev, "IRQ but nothing to do!, status %x\n", status); return; } if (status & I2C_STATUS_ERROR_MASK) goto out_err; if (!(status & I2C_STATUS_WR_BUFFER_FULL)) not_done = msm_i2c_fill_write_buffer(dev); if (status & I2C_STATUS_RD_BUFFER_FULL) msm_i2c_read_buffer(dev); if (dev->pos >= 0 && dev->cnt == 0) { if (dev->rem > 1) { dev->rem--; dev->msg++; dev->pos = -1; dev->cnt = dev->msg->len; } else if (!not_done && !dev->need_flush) goto out_complete; } return; out_err: dev_err(dev->dev, "error, status %x (%02X)\n", status, dev->msg->addr); dev->ret = -EIO; out_complete: complete(dev->complete); } static irqreturn_t msm_i2c_interrupt(int irq, void *devid) { struct msm_i2c_dev *dev = devid; spin_lock(&dev->lock); msm_i2c_interrupt_locked(dev); spin_unlock(&dev->lock); return IRQ_HANDLED; } static int msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn) { uint32_t retries = 0; while (retries != 200) { uint32_t status = readl(dev->base + I2C_STATUS); if (!(status & I2C_STATUS_BUS_ACTIVE)) { if (retries && warn) dev_warn(dev->dev, "Warning bus was busy (%d)\n", retries); return 0; } if (retries++ > 100) msleep(10); } dev_err(dev->dev, "Error waiting for notbusy\n"); return -ETIMEDOUT; } static int msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev) { int i; uint32_t status = readl(dev->base + I2C_STATUS); int gpio_clk, gpio_dat; bool gpio_clk_status = false; if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL))) return 0; msm_set_i2c_mux(true, &gpio_clk, &gpio_dat, 0, 0); if (status & I2C_STATUS_RD_BUFFER_FULL) { dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n", status, readl(dev->base + I2C_INTERFACE_SELECT)); writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); readl(dev->base + I2C_READ_DATA); } else if (status & I2C_STATUS_BUS_MASTER) { dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n", status, readl(dev->base + I2C_INTERFACE_SELECT)); writel(I2C_WRITE_DATA_LAST_BYTE | 0xff, dev->base + I2C_WRITE_DATA); } dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n", gpio_get_value(gpio_clk), gpio_get_value(gpio_dat)); for (i = 0; i < 9; i++) { if (gpio_get_value(gpio_dat) && gpio_clk_status) break; gpio_direction_output(gpio_clk, 0); udelay(5); gpio_direction_output(gpio_dat, 0); udelay(5); gpio_direction_input(gpio_clk); udelay(5); if (!gpio_get_value(gpio_clk)) udelay(20); if (!gpio_get_value(gpio_clk)) msleep(10); gpio_clk_status = gpio_get_value(gpio_clk); gpio_direction_input(gpio_dat); udelay(5); } msm_set_i2c_mux(false, NULL, NULL, dev->clk_drv_str, dev->dat_drv_str); udelay(10); status = readl(dev->base + I2C_STATUS); if (!(status & I2C_STATUS_BUS_ACTIVE)) { dev_info(dev->dev, "Bus busy cleared after %d clock cycles, " "status %x, intf %x\n", i, status, readl(dev->base + I2C_INTERFACE_SELECT)); return 0; } dev_warn(dev->dev, "Bus still busy, status %x, intf %x\n", status, readl(dev->base + I2C_INTERFACE_SELECT)); return -EBUSY; } static int msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { DECLARE_COMPLETION_ONSTACK(complete); struct msm_i2c_dev *dev = i2c_get_adapdata(adap); int ret; long timeout; unsigned long flags; /* * If there is an i2c_xfer after driver has been suspended, * grab wakelock to abort suspend. */ if (dev->is_suspended) wake_lock(&dev->wakelock); clk_enable(dev->clk); enable_irq(dev->irq); ret = msm_i2c_poll_notbusy(dev, 1); if (ret) { dev_err(dev->dev, "Still busy in starting xfer(%02X)\n", msgs->addr); if (!dev->skip_recover) { ret = msm_i2c_recover_bus_busy(dev); if (ret) goto err; } } spin_lock_irqsave(&dev->lock, flags); if (dev->flush_cnt) { dev_warn(dev->dev, "%d unrequested bytes read\n", dev->flush_cnt); } dev->msg = msgs; dev->rem = num; dev->pos = -1; dev->ret = num; dev->need_flush = false; dev->flush_cnt = 0; dev->cnt = msgs->len; dev->complete = &complete; msm_i2c_interrupt_locked(dev); spin_unlock_irqrestore(&dev->lock, flags); /* * Now that we've setup the xfer, the ISR will transfer the data * and wake us up with dev->err set if there was an error */ timeout = wait_for_completion_timeout(&complete, HZ); if (msm_i2c_poll_notbusy(dev, 0)) /* Read may not have stopped in time */ dev_err(dev->dev, "Still busy after xfer completion (%02X)\n", msgs->addr); spin_lock_irqsave(&dev->lock, flags); if (dev->flush_cnt) { dev_warn(dev->dev, "%d unrequested bytes read\n", dev->flush_cnt); } ret = dev->ret; dev->complete = NULL; dev->msg = NULL; dev->rem = 0; dev->pos = 0; dev->ret = 0; dev->flush_cnt = 0; dev->cnt = 0; spin_unlock_irqrestore(&dev->lock, flags); if (!timeout) { dev_err(dev->dev, "Transaction timed out\n"); ret = -ETIMEDOUT; } if (ret < 0) { dev_err(dev->dev, "Error during data xfer (%d) (%02X)\n", ret, msgs->addr); if (!dev->skip_recover) msm_i2c_recover_bus_busy(dev); } err: disable_irq(dev->irq); clk_disable(dev->clk); if (dev->is_suspended) wake_unlock(&dev->wakelock); return ret; } static u32 msm_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } static const struct i2c_algorithm msm_i2c_algo = { .master_xfer = msm_i2c_xfer, .functionality = msm_i2c_func, }; static int msm_i2c_probe(struct platform_device *pdev) { struct msm_i2c_dev *dev; struct resource *mem, *irq, *ioarea; struct msm_i2c_device_platform_data *pdata = pdev->dev.platform_data; int ret; int fs_div; int hs_div; int i2c_clk, i2c_clock; int clk_ctl; struct clk *clk; printk(KERN_INFO "msm_i2c_probe\n"); /* NOTE: driver uses the static register mapping */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { dev_err(&pdev->dev, "no mem resource?\n"); return -ENODEV; } irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "no irq resource?\n"); return -ENODEV; } ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, pdev->name); if (!ioarea) { dev_err(&pdev->dev, "I2C region already claimed\n"); return -EBUSY; } clk = clk_get(&pdev->dev, "i2c_clk"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "Could not get clock\n"); ret = PTR_ERR(clk); goto err_clk_get_failed; } dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err_alloc_dev_failed; } dev->dev = &pdev->dev; dev->irq = irq->start; dev->clk = clk; dev->base = ioremap(mem->start, (mem->end - mem->start) + 1); if (!dev->base) { ret = -ENOMEM; goto err_ioremap_failed; } spin_lock_init(&dev->lock); wake_lock_init(&dev->wakelock, WAKE_LOCK_SUSPEND, "i2c"); platform_set_drvdata(pdev, dev); if (pdata) { dev->clk_drv_str = pdata->clock_strength; dev->dat_drv_str = pdata->data_strength; if (pdata->i2c_clock < 100000 || pdata->i2c_clock > 400000) i2c_clock = 100000; else i2c_clock = pdata->i2c_clock; } else { dev->clk_drv_str = 0; dev->dat_drv_str = 0; i2c_clock = 100000; dev->skip_recover = 1; } if (!dev->skip_recover) msm_set_i2c_mux(false, NULL, NULL, dev->clk_drv_str, dev->dat_drv_str); clk_enable(clk); /* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */ /* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */ /* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */ i2c_clk = 19200000; /* input clock */ fs_div = ((i2c_clk / i2c_clock) / 2) - 3; hs_div = 3; clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff); writel(clk_ctl, dev->base + I2C_CLK_CTL); printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n", clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3))); clk_disable(clk); i2c_set_adapdata(&dev->adapter, dev); dev->adapter.algo = &msm_i2c_algo; strncpy(dev->adapter.name, "MSM I2C adapter", sizeof(dev->adapter.name)); dev->adapter.nr = pdev->id; ret = i2c_add_numbered_adapter(&dev->adapter); if (ret) { dev_err(&pdev->dev, "i2c_add_adapter failed\n"); goto err_i2c_add_adapter_failed; } ret = request_irq(dev->irq, msm_i2c_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TIMER, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto err_request_irq_failed; } disable_irq(dev->irq); return 0; /* free_irq(dev->irq, dev); */ err_request_irq_failed: i2c_del_adapter(&dev->adapter); err_i2c_add_adapter_failed: iounmap(dev->base); err_ioremap_failed: kfree(dev); err_alloc_dev_failed: clk_put(clk); err_clk_get_failed: release_mem_region(mem->start, (mem->end - mem->start) + 1); return ret; } static int msm_i2c_remove(struct platform_device *pdev) { struct msm_i2c_dev *dev = platform_get_drvdata(pdev); struct resource *mem; platform_set_drvdata(pdev, NULL); enable_irq(dev->irq); free_irq(dev->irq, dev); i2c_del_adapter(&dev->adapter); wake_lock_destroy(&dev->wakelock); clk_put(dev->clk); iounmap(dev->base); kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, (mem->end - mem->start) + 1); return 0; } static int msm_i2c_suspend_noirq(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct msm_i2c_dev *dev = platform_get_drvdata(pdev); /* Block to allow any i2c_xfers to finish */ i2c_lock_adapter(&dev->adapter); dev->is_suspended = true; i2c_unlock_adapter(&dev->adapter); return 0; } static int msm_i2c_resume_noirq(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct msm_i2c_dev *dev = platform_get_drvdata(pdev); /* Block to allow any i2c_xfers to finish */ i2c_lock_adapter(&dev->adapter); dev->is_suspended = false; i2c_unlock_adapter(&dev->adapter); return 0; } static struct dev_pm_ops msm_i2c_pm_ops = { .suspend_noirq = msm_i2c_suspend_noirq, .resume_noirq = msm_i2c_resume_noirq, }; static struct platform_driver msm_i2c_driver = { .probe = msm_i2c_probe, .remove = msm_i2c_remove, .driver = { .name = "msm_i2c", .owner = THIS_MODULE, .pm = &msm_i2c_pm_ops, }, }; /* I2C may be needed to bring up other drivers */ static int __init msm_i2c_init_driver(void) { return platform_driver_register(&msm_i2c_driver); } subsys_initcall(msm_i2c_init_driver); static void __exit msm_i2c_exit_driver(void) { platform_driver_unregister(&msm_i2c_driver); } module_exit(msm_i2c_exit_driver);