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 .pm = PUV3_I2C_PM, 274 } 275}; 276 277module_platform_driver(puv3_i2c_driver); 278 279MODULE_DESCRIPTION("PKUnity v3 I2C driver"); 280MODULE_LICENSE("GPL v2"); 281MODULE_ALIAS("platform:puv3_i2c"); 282