linux/arch/mips/mti-sead3/sead3-pic32-i2c-drv.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
   7 */
   8#include <linux/delay.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/spinlock.h>
  12#include <linux/platform_device.h>
  13#include <linux/init.h>
  14#include <linux/errno.h>
  15#include <linux/i2c.h>
  16#include <linux/slab.h>
  17
  18#define PIC32_I2CxCON           0x0000
  19#define PIC32_I2CxCONCLR        0x0004
  20#define PIC32_I2CxCONSET        0x0008
  21#define PIC32_I2CxCONINV        0x000C
  22#define  I2CCON_ON              (1<<15)
  23#define  I2CCON_FRZ             (1<<14)
  24#define  I2CCON_SIDL            (1<<13)
  25#define  I2CCON_SCLREL          (1<<12)
  26#define  I2CCON_STRICT          (1<<11)
  27#define  I2CCON_A10M            (1<<10)
  28#define  I2CCON_DISSLW          (1<<9)
  29#define  I2CCON_SMEN            (1<<8)
  30#define  I2CCON_GCEN            (1<<7)
  31#define  I2CCON_STREN           (1<<6)
  32#define  I2CCON_ACKDT           (1<<5)
  33#define  I2CCON_ACKEN           (1<<4)
  34#define  I2CCON_RCEN            (1<<3)
  35#define  I2CCON_PEN             (1<<2)
  36#define  I2CCON_RSEN            (1<<1)
  37#define  I2CCON_SEN             (1<<0)
  38
  39#define PIC32_I2CxSTAT          0x0010
  40#define PIC32_I2CxSTATCLR       0x0014
  41#define PIC32_I2CxSTATSET       0x0018
  42#define PIC32_I2CxSTATINV       0x001C
  43#define  I2CSTAT_ACKSTAT        (1<<15)
  44#define  I2CSTAT_TRSTAT         (1<<14)
  45#define  I2CSTAT_BCL            (1<<10)
  46#define  I2CSTAT_GCSTAT         (1<<9)
  47#define  I2CSTAT_ADD10          (1<<8)
  48#define  I2CSTAT_IWCOL          (1<<7)
  49#define  I2CSTAT_I2COV          (1<<6)
  50#define  I2CSTAT_DA             (1<<5)
  51#define  I2CSTAT_P              (1<<4)
  52#define  I2CSTAT_S              (1<<3)
  53#define  I2CSTAT_RW             (1<<2)
  54#define  I2CSTAT_RBF            (1<<1)
  55#define  I2CSTAT_TBF            (1<<0)
  56
  57#define PIC32_I2CxADD           0x0020
  58#define PIC32_I2CxADDCLR        0x0024
  59#define PIC32_I2CxADDSET        0x0028
  60#define PIC32_I2CxADDINV        0x002C
  61#define PIC32_I2CxMSK           0x0030
  62#define PIC32_I2CxMSKCLR        0x0034
  63#define PIC32_I2CxMSKSET        0x0038
  64#define PIC32_I2CxMSKINV        0x003C
  65#define PIC32_I2CxBRG           0x0040
  66#define PIC32_I2CxBRGCLR        0x0044
  67#define PIC32_I2CxBRGSET        0x0048
  68#define PIC32_I2CxBRGINV        0x004C
  69#define PIC32_I2CxTRN           0x0050
  70#define PIC32_I2CxTRNCLR        0x0054
  71#define PIC32_I2CxTRNSET        0x0058
  72#define PIC32_I2CxTRNINV        0x005C
  73#define PIC32_I2CxRCV           0x0060
  74
  75struct i2c_platform_data {
  76        u32     base;
  77        struct i2c_adapter adap;
  78        u32     xfer_timeout;
  79        u32     ack_timeout;
  80        u32     ctl_timeout;
  81};
  82
  83extern u32 pic32_bus_readl(u32 reg);
  84extern void pic32_bus_writel(u32 val, u32 reg);
  85
  86static inline void
  87StartI2C(struct i2c_platform_data *adap)
  88{
  89        pr_debug("StartI2C\n");
  90        pic32_bus_writel(I2CCON_SEN, adap->base + PIC32_I2CxCONSET);
  91}
  92
  93static inline void
  94StopI2C(struct i2c_platform_data *adap)
  95{
  96        pr_debug("StopI2C\n");
  97        pic32_bus_writel(I2CCON_PEN, adap->base + PIC32_I2CxCONSET);
  98}
  99
 100static inline void
 101AckI2C(struct i2c_platform_data *adap)
 102{
 103        pr_debug("AckI2C\n");
 104        pic32_bus_writel(I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR);
 105        pic32_bus_writel(I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET);
 106}
 107
 108static inline void
 109NotAckI2C(struct i2c_platform_data *adap)
 110{
 111        pr_debug("NakI2C\n");
 112        pic32_bus_writel(I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET);
 113        pic32_bus_writel(I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET);
 114}
 115
 116static inline int
 117IdleI2C(struct i2c_platform_data *adap)
 118{
 119        int i;
 120
 121        pr_debug("IdleI2C\n");
 122        for (i = 0; i < adap->ctl_timeout; i++) {
 123                if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) &
 124                     (I2CCON_ACKEN | I2CCON_RCEN | I2CCON_PEN | I2CCON_RSEN |
 125                      I2CCON_SEN)) == 0) &&
 126                    ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) &
 127                     (I2CSTAT_TRSTAT)) == 0))
 128                        return 0;
 129                udelay(1);
 130        }
 131        return -ETIMEDOUT;
 132}
 133
 134static inline u32
 135MasterWriteI2C(struct i2c_platform_data *adap, u32 byte)
 136{
 137        pr_debug("MasterWriteI2C\n");
 138
 139        pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN);
 140
 141        return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & I2CSTAT_IWCOL;
 142}
 143
 144static inline u32
 145MasterReadI2C(struct i2c_platform_data *adap)
 146{
 147        pr_debug("MasterReadI2C\n");
 148
 149        pic32_bus_writel(I2CCON_RCEN, adap->base + PIC32_I2CxCONSET);
 150
 151        while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & I2CCON_RCEN)
 152                ;
 153
 154        pic32_bus_writel(I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR);
 155
 156        return pic32_bus_readl(adap->base + PIC32_I2CxRCV);
 157}
 158
 159static int
 160do_address(struct i2c_platform_data *adap, unsigned int addr, int rd)
 161{
 162        pr_debug("doaddress\n");
 163
 164        IdleI2C(adap);
 165        StartI2C(adap);
 166        IdleI2C(adap);
 167
 168        addr <<= 1;
 169        if (rd)
 170                addr |= 1;
 171
 172        if (MasterWriteI2C(adap, addr))
 173                return -EIO;
 174        IdleI2C(adap);
 175        if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & I2CSTAT_ACKSTAT)
 176                return -EIO;
 177        return 0;
 178}
 179
 180static int
 181i2c_read(struct i2c_platform_data *adap, unsigned char *buf,
 182                    unsigned int len)
 183{
 184        int     i;
 185        u32     data;
 186
 187        pr_debug("i2c_read\n");
 188
 189        i = 0;
 190        while (i < len) {
 191                data = MasterReadI2C(adap);
 192                buf[i++] = data;
 193                if (i < len)
 194                        AckI2C(adap);
 195                else
 196                        NotAckI2C(adap);
 197        }
 198
 199        StopI2C(adap);
 200        IdleI2C(adap);
 201        return 0;
 202}
 203
 204static int
 205i2c_write(struct i2c_platform_data *adap, unsigned char *buf,
 206                     unsigned int len)
 207{
 208        int     i;
 209        u32     data;
 210
 211        pr_debug("i2c_write\n");
 212
 213        i = 0;
 214        while (i < len) {
 215                data = buf[i];
 216                if (MasterWriteI2C(adap, data))
 217                        return -EIO;
 218                IdleI2C(adap);
 219                if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) &
 220                    I2CSTAT_ACKSTAT)
 221                        return -EIO;
 222                i++;
 223        }
 224
 225        StopI2C(adap);
 226        IdleI2C(adap);
 227        return 0;
 228}
 229
 230static int
 231platform_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
 232{
 233        struct i2c_platform_data *adap = i2c_adap->algo_data;
 234        struct i2c_msg *p;
 235        int i, err = 0;
 236
 237        pr_debug("platform_xfer\n");
 238        for (i = 0; i < num; i++) {
 239#define __BUFSIZE 80
 240                int ii;
 241                static char buf[__BUFSIZE];
 242                char *b = buf;
 243
 244                p = &msgs[i];
 245                b += sprintf(buf, " [%d bytes]", p->len);
 246                if ((p->flags & I2C_M_RD) == 0) {
 247                        for (ii = 0; ii < p->len; ii++) {
 248                                if (b < &buf[__BUFSIZE-4]) {
 249                                        b += sprintf(b, " %02x", p->buf[ii]);
 250                                } else {
 251                                        strcat(b, "...");
 252                                        break;
 253                                }
 254                        }
 255                }
 256                pr_debug("xfer%d: DevAddr: %04x Op:%s Data:%s\n", i, p->addr,
 257                         (p->flags & I2C_M_RD) ? "Rd" : "Wr", buf);
 258        }
 259
 260
 261        for (i = 0; !err && i < num; i++) {
 262                p = &msgs[i];
 263                err = do_address(adap, p->addr, p->flags & I2C_M_RD);
 264                if (err || !p->len)
 265                        continue;
 266                if (p->flags & I2C_M_RD)
 267                        err = i2c_read(adap, p->buf, p->len);
 268                else
 269                        err = i2c_write(adap, p->buf, p->len);
 270        }
 271
 272        /* Return the number of messages processed, or the error code. */
 273        if (err == 0)
 274                err = num;
 275
 276        return err;
 277}
 278
 279static u32
 280platform_func(struct i2c_adapter *adap)
 281{
 282        pr_debug("platform_algo\n");
 283        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 284}
 285
 286static const struct i2c_algorithm platform_algo = {
 287        .master_xfer    = platform_xfer,
 288        .functionality  = platform_func,
 289};
 290
 291static void i2c_platform_setup(struct i2c_platform_data *priv)
 292{
 293        pr_debug("i2c_platform_setup\n");
 294
 295        pic32_bus_writel(500, priv->base + PIC32_I2CxBRG);
 296        pic32_bus_writel(I2CCON_ON, priv->base + PIC32_I2CxCONCLR);
 297        pic32_bus_writel(I2CCON_ON, priv->base + PIC32_I2CxCONSET);
 298        pic32_bus_writel((I2CSTAT_BCL | I2CSTAT_IWCOL),
 299                (priv->base + PIC32_I2CxSTATCLR));
 300}
 301
 302static void i2c_platform_disable(struct i2c_platform_data *priv)
 303{
 304        pr_debug("i2c_platform_disable\n");
 305}
 306
 307static int i2c_platform_probe(struct platform_device *pdev)
 308{
 309        struct i2c_platform_data *priv;
 310        struct resource *r;
 311        int ret;
 312
 313        pr_debug("i2c_platform_probe\n");
 314        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 315        if (!r) {
 316                ret = -ENODEV;
 317                goto out;
 318        }
 319
 320        priv = kzalloc(sizeof(struct i2c_platform_data), GFP_KERNEL);
 321        if (!priv) {
 322                ret = -ENOMEM;
 323                goto out;
 324        }
 325
 326        /* FIXME: need to allocate resource in PIC32 space */
 327#if 0
 328        priv->base = bus_request_region(r->start, resource_size(r),
 329                                          pdev->name);
 330#else
 331        priv->base = r->start;
 332#endif
 333        if (!priv->base) {
 334                ret = -EBUSY;
 335                goto out_mem;
 336        }
 337
 338        priv->xfer_timeout = 200;
 339        priv->ack_timeout = 200;
 340        priv->ctl_timeout = 200;
 341
 342        priv->adap.nr = pdev->id;
 343        priv->adap.algo = &platform_algo;
 344        priv->adap.algo_data = priv;
 345        priv->adap.dev.parent = &pdev->dev;
 346        strlcpy(priv->adap.name, "PIC32 I2C", sizeof(priv->adap.name));
 347
 348        i2c_platform_setup(priv);
 349
 350        ret = i2c_add_numbered_adapter(&priv->adap);
 351        if (ret == 0) {
 352                platform_set_drvdata(pdev, priv);
 353                return 0;
 354        }
 355
 356        i2c_platform_disable(priv);
 357
 358out_mem:
 359        kfree(priv);
 360out:
 361        return ret;
 362}
 363
 364static int i2c_platform_remove(struct platform_device *pdev)
 365{
 366        struct i2c_platform_data *priv = platform_get_drvdata(pdev);
 367
 368        pr_debug("i2c_platform_remove\n");
 369        platform_set_drvdata(pdev, NULL);
 370        i2c_del_adapter(&priv->adap);
 371        i2c_platform_disable(priv);
 372        kfree(priv);
 373        return 0;
 374}
 375
 376#ifdef CONFIG_PM
 377static int
 378i2c_platform_suspend(struct platform_device *pdev, pm_message_t state)
 379{
 380        struct i2c_platform_data *priv = platform_get_drvdata(pdev);
 381
 382        dev_dbg(&pdev->dev, "i2c_platform_disable\n");
 383        i2c_platform_disable(priv);
 384
 385        return 0;
 386}
 387
 388static int
 389i2c_platform_resume(struct platform_device *pdev)
 390{
 391        struct i2c_platform_data *priv = platform_get_drvdata(pdev);
 392
 393        dev_dbg(&pdev->dev, "i2c_platform_setup\n");
 394        i2c_platform_setup(priv);
 395
 396        return 0;
 397}
 398#else
 399#define i2c_platform_suspend    NULL
 400#define i2c_platform_resume     NULL
 401#endif
 402
 403static struct platform_driver i2c_platform_driver = {
 404        .driver = {
 405                .name   = "i2c_pic32",
 406                .owner  = THIS_MODULE,
 407        },
 408        .probe          = i2c_platform_probe,
 409        .remove         = i2c_platform_remove,
 410        .suspend        = i2c_platform_suspend,
 411        .resume         = i2c_platform_resume,
 412};
 413
 414static int __init
 415i2c_platform_init(void)
 416{
 417        pr_debug("i2c_platform_init\n");
 418        return platform_driver_register(&i2c_platform_driver);
 419}
 420
 421static void __exit
 422i2c_platform_exit(void)
 423{
 424        pr_debug("i2c_platform_exit\n");
 425        platform_driver_unregister(&i2c_platform_driver);
 426}
 427
 428MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC.");
 429MODULE_DESCRIPTION("PIC32 I2C driver");
 430MODULE_LICENSE("GPL");
 431
 432module_init(i2c_platform_init);
 433module_exit(i2c_platform_exit);
 434