linux/drivers/i2c/busses/i2c-puv3.c
<<
>>
Prefs
   1/*
   2 * I2C driver for PKUnity-v3 SoC
   3 * Code specific to PKUnity SoC and UniCore ISA
   4 *
   5 *      Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
   6 *      Copyright (C) 2001-2010 Guan Xuetao
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/err.h>
  16#include <linux/slab.h>
  17#include <linux/types.h>
  18#include <linux/delay.h>
  19#include <linux/i2c.h>
  20#include <linux/clk.h>
  21#include <linux/platform_device.h>
  22#include <linux/io.h>
  23#include <mach/hardware.h>
  24
  25/*
  26 * Poll the i2c status register until the specified bit is set.
  27 * Returns 0 if timed out (100 msec).
  28 */
  29static short poll_status(unsigned long bit)
  30{
  31        int loop_cntr = 1000;
  32
  33        if (bit & I2C_STATUS_TFNF) {
  34                do {
  35                        udelay(10);
  36                } while (!(readl(I2C_STATUS) & bit) && (--loop_cntr > 0));
  37        } else {
  38                /* RXRDY handler */
  39                do {
  40                        if (readl(I2C_TAR) == I2C_TAR_EEPROM)
  41                                msleep(20);
  42                        else
  43                                udelay(10);
  44                } while (!(readl(I2C_RXFLR) & 0xf) && (--loop_cntr > 0));
  45        }
  46
  47        return (loop_cntr > 0);
  48}
  49
  50static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
  51{
  52        int i2c_reg = *buf;
  53
  54        /* Read data */
  55        while (length--) {
  56                if (!poll_status(I2C_STATUS_TFNF)) {
  57                        dev_dbg(&adap->dev, "Tx FIFO Not Full timeout\n");
  58                        return -ETIMEDOUT;
  59                }
  60
  61                /* send addr */
  62                writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
  63
  64                /* get ready to next write */
  65                i2c_reg++;
  66
  67                /* send read CMD */
  68                writel(I2C_DATACMD_READ, I2C_DATACMD);
  69
  70                /* wait until the Rx FIFO have available */
  71                if (!poll_status(I2C_STATUS_RFNE)) {
  72                        dev_dbg(&adap->dev, "RXRDY timeout\n");
  73                        return -ETIMEDOUT;
  74                }
  75
  76                /* read the data to buf */
  77                *buf = (readl(I2C_DATACMD) & I2C_DATACMD_DAT_MASK);
  78                buf++;
  79        }
  80
  81        return 0;
  82}
  83
  84static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
  85{
  86        int i2c_reg = *buf;
  87
  88        /* Do nothing but storing the reg_num to a static variable */
  89        if (i2c_reg == -1) {
  90                printk(KERN_WARNING "Error i2c reg\n");
  91                return -ETIMEDOUT;
  92        }
  93
  94        if (length == 1)
  95                return 0;
  96
  97        buf++;
  98        length--;
  99        while (length--) {
 100                /* send addr */
 101                writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
 102
 103                /* send write CMD */
 104                writel(*buf | I2C_DATACMD_WRITE, I2C_DATACMD);
 105
 106                /* wait until the Rx FIFO have available */
 107                msleep(20);
 108
 109                /* read the data to buf */
 110                i2c_reg++;
 111                buf++;
 112        }
 113
 114        return 0;
 115}
 116
 117/*
 118 * Generic i2c master transfer entrypoint.
 119 *
 120 */
 121static int puv3_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
 122                int num)
 123{
 124        int i, ret;
 125        unsigned char swap;
 126
 127        /* Disable i2c */
 128        writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
 129
 130        /* Set the work mode and speed*/
 131        writel(I2C_CON_MASTER | I2C_CON_SPEED_STD | I2C_CON_SLAVEDISABLE, I2C_CON);
 132
 133        writel(pmsg->addr, I2C_TAR);
 134
 135        /* Enable i2c */
 136        writel(I2C_ENABLE_ENABLE, I2C_ENABLE);
 137
 138        dev_dbg(&adap->dev, "puv3_i2c_xfer: processing %d messages:\n", num);
 139
 140        for (i = 0; i < num; i++) {
 141                dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
 142                        pmsg->flags & I2C_M_RD ? "read" : "writ",
 143                        pmsg->len, pmsg->len > 1 ? "s" : "",
 144                        pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr);
 145
 146                if (pmsg->len && pmsg->buf) {   /* sanity check */
 147                        if (pmsg->flags & I2C_M_RD)
 148                                ret = xfer_read(adap, pmsg->buf, pmsg->len);
 149                        else
 150                                ret = xfer_write(adap, pmsg->buf, pmsg->len);
 151
 152                        if (ret)
 153                                return ret;
 154
 155                }
 156                dev_dbg(&adap->dev, "transfer complete\n");
 157                pmsg++;         /* next message */
 158        }
 159
 160        /* XXX: fixup be16_to_cpu in bq27x00_battery.c */
 161        if (pmsg->addr == I2C_TAR_PWIC) {
 162                swap = pmsg->buf[0];
 163                pmsg->buf[0] = pmsg->buf[1];
 164                pmsg->buf[1] = swap;
 165        }
 166
 167        return i;
 168}
 169
 170/*
 171 * Return list of supported functionality.
 172 */
 173static u32 puv3_i2c_func(struct i2c_adapter *adapter)
 174{
 175        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 176}
 177
 178static struct i2c_algorithm puv3_i2c_algorithm = {
 179        .master_xfer    = puv3_i2c_xfer,
 180        .functionality  = puv3_i2c_func,
 181};
 182
 183/*
 184 * Main initialization routine.
 185 */
 186static int puv3_i2c_probe(struct platform_device *pdev)
 187{
 188        struct i2c_adapter *adapter;
 189        struct resource *mem;
 190        int rc;
 191
 192        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 193        if (!mem)
 194                return -ENODEV;
 195
 196        if (!request_mem_region(mem->start, resource_size(mem), "puv3_i2c"))
 197                return -EBUSY;
 198
 199        adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
 200        if (adapter == NULL) {
 201                dev_err(&pdev->dev, "can't allocate interface!\n");
 202                rc = -ENOMEM;
 203                goto fail_nomem;
 204        }
 205        snprintf(adapter->name, sizeof(adapter->name), "PUV3-I2C at 0x%08x",
 206                        mem->start);
 207        adapter->algo = &puv3_i2c_algorithm;
 208        adapter->class = I2C_CLASS_HWMON;
 209        adapter->dev.parent = &pdev->dev;
 210
 211        platform_set_drvdata(pdev, adapter);
 212
 213        adapter->nr = pdev->id;
 214        rc = i2c_add_numbered_adapter(adapter);
 215        if (rc) {
 216                dev_err(&pdev->dev, "Adapter '%s' registration failed\n",
 217                                adapter->name);
 218                goto fail_add_adapter;
 219        }
 220
 221        dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n");
 222        return 0;
 223
 224fail_add_adapter:
 225        kfree(adapter);
 226fail_nomem:
 227        release_mem_region(mem->start, resource_size(mem));
 228
 229        return rc;
 230}
 231
 232static int puv3_i2c_remove(struct platform_device *pdev)
 233{
 234        struct i2c_adapter *adapter = platform_get_drvdata(pdev);
 235        struct resource *mem;
 236
 237        i2c_del_adapter(adapter);
 238
 239        put_device(&pdev->dev);
 240
 241        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 242        release_mem_region(mem->start, resource_size(mem));
 243
 244        return 0;
 245}
 246
 247#ifdef CONFIG_PM_SLEEP
 248static int puv3_i2c_suspend(struct device *dev)
 249{
 250        int poll_count;
 251        /* Disable the IIC */
 252        writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
 253        for (poll_count = 0; poll_count < 50; poll_count++) {
 254                if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE)
 255                        udelay(25);
 256        }
 257
 258        return 0;
 259}
 260
 261static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL);
 262#define PUV3_I2C_PM     (&puv3_i2c_pm)
 263
 264#else
 265#define PUV3_I2C_PM     NULL
 266#endif
 267
 268static struct platform_driver puv3_i2c_driver = {
 269        .probe          = puv3_i2c_probe,
 270        .remove         = puv3_i2c_remove,
 271        .driver         = {
 272                .name   = "PKUnity-v3-I2C",
 273                .owner  = THIS_MODULE,
 274                .pm     = PUV3_I2C_PM,
 275        }
 276};
 277
 278module_platform_driver(puv3_i2c_driver);
 279
 280MODULE_DESCRIPTION("PKUnity v3 I2C driver");
 281MODULE_LICENSE("GPL v2");
 282MODULE_ALIAS("platform:puv3_i2c");
 283