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 v4.13-rc2 278 lines 6.1 kB view raw
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 goto fail_add_adapter; 217 218 dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n"); 219 return 0; 220 221fail_add_adapter: 222 kfree(adapter); 223fail_nomem: 224 release_mem_region(mem->start, resource_size(mem)); 225 226 return rc; 227} 228 229static int puv3_i2c_remove(struct platform_device *pdev) 230{ 231 struct i2c_adapter *adapter = platform_get_drvdata(pdev); 232 struct resource *mem; 233 234 i2c_del_adapter(adapter); 235 236 put_device(&pdev->dev); 237 238 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 239 release_mem_region(mem->start, resource_size(mem)); 240 241 return 0; 242} 243 244#ifdef CONFIG_PM_SLEEP 245static int puv3_i2c_suspend(struct device *dev) 246{ 247 int poll_count; 248 /* Disable the IIC */ 249 writel(I2C_ENABLE_DISABLE, I2C_ENABLE); 250 for (poll_count = 0; poll_count < 50; poll_count++) { 251 if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE) 252 udelay(25); 253 } 254 255 return 0; 256} 257 258static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL); 259#define PUV3_I2C_PM (&puv3_i2c_pm) 260 261#else 262#define PUV3_I2C_PM NULL 263#endif 264 265static struct platform_driver puv3_i2c_driver = { 266 .probe = puv3_i2c_probe, 267 .remove = puv3_i2c_remove, 268 .driver = { 269 .name = "PKUnity-v3-I2C", 270 .pm = PUV3_I2C_PM, 271 } 272}; 273 274module_platform_driver(puv3_i2c_driver); 275 276MODULE_DESCRIPTION("PKUnity v3 I2C driver"); 277MODULE_LICENSE("GPL v2"); 278MODULE_ALIAS("platform:puv3_i2c");