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