Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.10 405 lines 9.4 kB view raw
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/init.h> 9#include <linux/module.h> 10#include <linux/slab.h> 11#include <linux/delay.h> 12#include <linux/i2c.h> 13#include <linux/platform_device.h> 14 15#define PIC32_I2CxCON 0x0000 16#define PIC32_I2CCON_ON (1<<15) 17#define PIC32_I2CCON_ACKDT (1<<5) 18#define PIC32_I2CCON_ACKEN (1<<4) 19#define PIC32_I2CCON_RCEN (1<<3) 20#define PIC32_I2CCON_PEN (1<<2) 21#define PIC32_I2CCON_RSEN (1<<1) 22#define PIC32_I2CCON_SEN (1<<0) 23#define PIC32_I2CxCONCLR 0x0004 24#define PIC32_I2CxCONSET 0x0008 25#define PIC32_I2CxSTAT 0x0010 26#define PIC32_I2CxSTATCLR 0x0014 27#define PIC32_I2CSTAT_ACKSTAT (1<<15) 28#define PIC32_I2CSTAT_TRSTAT (1<<14) 29#define PIC32_I2CSTAT_BCL (1<<10) 30#define PIC32_I2CSTAT_IWCOL (1<<7) 31#define PIC32_I2CSTAT_I2COV (1<<6) 32#define PIC32_I2CxBRG 0x0040 33#define PIC32_I2CxTRN 0x0050 34#define PIC32_I2CxRCV 0x0060 35 36static DEFINE_SPINLOCK(pic32_bus_lock); 37 38static void __iomem *bus_xfer = (void __iomem *)0xbf000600; 39static void __iomem *bus_status = (void __iomem *)0xbf000060; 40 41#define DELAY() udelay(100) 42 43static inline unsigned int ioready(void) 44{ 45 return readl(bus_status) & 1; 46} 47 48static inline void wait_ioready(void) 49{ 50 do { } while (!ioready()); 51} 52 53static inline void wait_ioclear(void) 54{ 55 do { } while (ioready()); 56} 57 58static inline void check_ioclear(void) 59{ 60 if (ioready()) { 61 do { 62 (void) readl(bus_xfer); 63 DELAY(); 64 } while (ioready()); 65 } 66} 67 68static u32 pic32_bus_readl(u32 reg) 69{ 70 unsigned long flags; 71 u32 status, val; 72 73 spin_lock_irqsave(&pic32_bus_lock, flags); 74 75 check_ioclear(); 76 writel((0x01 << 24) | (reg & 0x00ffffff), bus_xfer); 77 DELAY(); 78 wait_ioready(); 79 status = readl(bus_xfer); 80 DELAY(); 81 val = readl(bus_xfer); 82 wait_ioclear(); 83 84 spin_unlock_irqrestore(&pic32_bus_lock, flags); 85 86 return val; 87} 88 89static void pic32_bus_writel(u32 val, u32 reg) 90{ 91 unsigned long flags; 92 u32 status; 93 94 spin_lock_irqsave(&pic32_bus_lock, flags); 95 96 check_ioclear(); 97 writel((0x10 << 24) | (reg & 0x00ffffff), bus_xfer); 98 DELAY(); 99 writel(val, bus_xfer); 100 DELAY(); 101 wait_ioready(); 102 status = readl(bus_xfer); 103 wait_ioclear(); 104 105 spin_unlock_irqrestore(&pic32_bus_lock, flags); 106} 107 108struct pic32_i2c_platform_data { 109 u32 base; 110 struct i2c_adapter adap; 111 u32 xfer_timeout; 112 u32 ack_timeout; 113 u32 ctl_timeout; 114}; 115 116static inline void pic32_i2c_start(struct pic32_i2c_platform_data *adap) 117{ 118 pic32_bus_writel(PIC32_I2CCON_SEN, adap->base + PIC32_I2CxCONSET); 119} 120 121static inline void pic32_i2c_stop(struct pic32_i2c_platform_data *adap) 122{ 123 pic32_bus_writel(PIC32_I2CCON_PEN, adap->base + PIC32_I2CxCONSET); 124} 125 126static inline void pic32_i2c_ack(struct pic32_i2c_platform_data *adap) 127{ 128 pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR); 129 pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); 130} 131 132static inline void pic32_i2c_nack(struct pic32_i2c_platform_data *adap) 133{ 134 pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET); 135 pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); 136} 137 138static inline int pic32_i2c_idle(struct pic32_i2c_platform_data *adap) 139{ 140 int i; 141 142 for (i = 0; i < adap->ctl_timeout; i++) { 143 if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) & 144 (PIC32_I2CCON_ACKEN | PIC32_I2CCON_RCEN | 145 PIC32_I2CCON_PEN | PIC32_I2CCON_RSEN | 146 PIC32_I2CCON_SEN)) == 0) && 147 ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & 148 (PIC32_I2CSTAT_TRSTAT)) == 0)) 149 return 0; 150 udelay(1); 151 } 152 return -ETIMEDOUT; 153} 154 155static inline u32 pic32_i2c_master_write(struct pic32_i2c_platform_data *adap, 156 u32 byte) 157{ 158 pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN); 159 return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & 160 PIC32_I2CSTAT_IWCOL; 161} 162 163static inline u32 pic32_i2c_master_read(struct pic32_i2c_platform_data *adap) 164{ 165 pic32_bus_writel(PIC32_I2CCON_RCEN, adap->base + PIC32_I2CxCONSET); 166 while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & PIC32_I2CCON_RCEN) 167 ; 168 pic32_bus_writel(PIC32_I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR); 169 return pic32_bus_readl(adap->base + PIC32_I2CxRCV); 170} 171 172static int pic32_i2c_address(struct pic32_i2c_platform_data *adap, 173 unsigned int addr, int rd) 174{ 175 pic32_i2c_idle(adap); 176 pic32_i2c_start(adap); 177 pic32_i2c_idle(adap); 178 179 addr <<= 1; 180 if (rd) 181 addr |= 1; 182 183 if (pic32_i2c_master_write(adap, addr)) 184 return -EIO; 185 pic32_i2c_idle(adap); 186 if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & 187 PIC32_I2CSTAT_ACKSTAT) 188 return -EIO; 189 return 0; 190} 191 192static int sead3_i2c_read(struct pic32_i2c_platform_data *adap, 193 unsigned char *buf, unsigned int len) 194{ 195 u32 data; 196 int i; 197 198 i = 0; 199 while (i < len) { 200 data = pic32_i2c_master_read(adap); 201 buf[i++] = data; 202 if (i < len) 203 pic32_i2c_ack(adap); 204 else 205 pic32_i2c_nack(adap); 206 } 207 208 pic32_i2c_stop(adap); 209 pic32_i2c_idle(adap); 210 return 0; 211} 212 213static int sead3_i2c_write(struct pic32_i2c_platform_data *adap, 214 unsigned char *buf, unsigned int len) 215{ 216 int i; 217 u32 data; 218 219 i = 0; 220 while (i < len) { 221 data = buf[i]; 222 if (pic32_i2c_master_write(adap, data)) 223 return -EIO; 224 pic32_i2c_idle(adap); 225 if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & 226 PIC32_I2CSTAT_ACKSTAT) 227 return -EIO; 228 i++; 229 } 230 231 pic32_i2c_stop(adap); 232 pic32_i2c_idle(adap); 233 return 0; 234} 235 236static int sead3_pic32_platform_xfer(struct i2c_adapter *i2c_adap, 237 struct i2c_msg *msgs, int num) 238{ 239 struct pic32_i2c_platform_data *adap = i2c_adap->algo_data; 240 struct i2c_msg *p; 241 int i, err = 0; 242 243 for (i = 0; i < num; i++) { 244#define __BUFSIZE 80 245 int ii; 246 static char buf[__BUFSIZE]; 247 char *b = buf; 248 249 p = &msgs[i]; 250 b += sprintf(buf, " [%d bytes]", p->len); 251 if ((p->flags & I2C_M_RD) == 0) { 252 for (ii = 0; ii < p->len; ii++) { 253 if (b < &buf[__BUFSIZE-4]) { 254 b += sprintf(b, " %02x", p->buf[ii]); 255 } else { 256 strcat(b, "..."); 257 break; 258 } 259 } 260 } 261 } 262 263 for (i = 0; !err && i < num; i++) { 264 p = &msgs[i]; 265 err = pic32_i2c_address(adap, p->addr, p->flags & I2C_M_RD); 266 if (err || !p->len) 267 continue; 268 if (p->flags & I2C_M_RD) 269 err = sead3_i2c_read(adap, p->buf, p->len); 270 else 271 err = sead3_i2c_write(adap, p->buf, p->len); 272 } 273 274 /* Return the number of messages processed, or the error code. */ 275 if (err == 0) 276 err = num; 277 278 return err; 279} 280 281static u32 sead3_pic32_platform_func(struct i2c_adapter *adap) 282{ 283 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 284} 285 286static const struct i2c_algorithm sead3_platform_algo = { 287 .master_xfer = sead3_pic32_platform_xfer, 288 .functionality = sead3_pic32_platform_func, 289}; 290 291static void sead3_i2c_platform_setup(struct pic32_i2c_platform_data *priv) 292{ 293 pic32_bus_writel(500, priv->base + PIC32_I2CxBRG); 294 pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONCLR); 295 pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONSET); 296 pic32_bus_writel(PIC32_I2CSTAT_BCL | PIC32_I2CSTAT_IWCOL, 297 priv->base + PIC32_I2CxSTATCLR); 298} 299 300static int sead3_i2c_platform_probe(struct platform_device *pdev) 301{ 302 struct pic32_i2c_platform_data *priv; 303 struct resource *r; 304 int ret; 305 306 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 307 if (!r) { 308 ret = -ENODEV; 309 goto out; 310 } 311 312 priv = kzalloc(sizeof(struct pic32_i2c_platform_data), GFP_KERNEL); 313 if (!priv) { 314 ret = -ENOMEM; 315 goto out; 316 } 317 318 priv->base = r->start; 319 if (!priv->base) { 320 ret = -EBUSY; 321 goto out_mem; 322 } 323 324 priv->xfer_timeout = 200; 325 priv->ack_timeout = 200; 326 priv->ctl_timeout = 200; 327 328 priv->adap.nr = pdev->id; 329 priv->adap.algo = &sead3_platform_algo; 330 priv->adap.algo_data = priv; 331 priv->adap.dev.parent = &pdev->dev; 332 strlcpy(priv->adap.name, "SEAD3 PIC32", sizeof(priv->adap.name)); 333 334 sead3_i2c_platform_setup(priv); 335 336 ret = i2c_add_numbered_adapter(&priv->adap); 337 if (ret == 0) { 338 platform_set_drvdata(pdev, priv); 339 return 0; 340 } 341 342out_mem: 343 kfree(priv); 344out: 345 return ret; 346} 347 348static int sead3_i2c_platform_remove(struct platform_device *pdev) 349{ 350 struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); 351 352 platform_set_drvdata(pdev, NULL); 353 i2c_del_adapter(&priv->adap); 354 kfree(priv); 355 return 0; 356} 357 358#ifdef CONFIG_PM 359static int sead3_i2c_platform_suspend(struct platform_device *pdev, 360 pm_message_t state) 361{ 362 dev_dbg(&pdev->dev, "i2c_platform_disable\n"); 363 return 0; 364} 365 366static int sead3_i2c_platform_resume(struct platform_device *pdev) 367{ 368 struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); 369 370 dev_dbg(&pdev->dev, "sead3_i2c_platform_setup\n"); 371 sead3_i2c_platform_setup(priv); 372 373 return 0; 374} 375#else 376#define sead3_i2c_platform_suspend NULL 377#define sead3_i2c_platform_resume NULL 378#endif 379 380static struct platform_driver sead3_i2c_platform_driver = { 381 .driver = { 382 .name = "sead3-i2c", 383 .owner = THIS_MODULE, 384 }, 385 .probe = sead3_i2c_platform_probe, 386 .remove = sead3_i2c_platform_remove, 387 .suspend = sead3_i2c_platform_suspend, 388 .resume = sead3_i2c_platform_resume, 389}; 390 391static int __init sead3_i2c_platform_init(void) 392{ 393 return platform_driver_register(&sead3_i2c_platform_driver); 394} 395module_init(sead3_i2c_platform_init); 396 397static void __exit sead3_i2c_platform_exit(void) 398{ 399 platform_driver_unregister(&sead3_i2c_platform_driver); 400} 401module_exit(sead3_i2c_platform_exit); 402 403MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC."); 404MODULE_DESCRIPTION("SEAD3 PIC32 I2C driver"); 405MODULE_LICENSE("GPL");