1/* 2 * Copyright (C) 2005 Simtec Electronics 3 * Ben Dooks <ben@simtec.co.uk> 4 * 5 * Simtec Generic I2C Controller 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15*/ 16 17#include <linux/kernel.h> 18#include <linux/module.h> 19#include <linux/delay.h> 20#include <linux/platform_device.h> 21#include <linux/slab.h> 22#include <linux/io.h> 23 24#include <linux/i2c.h> 25#include <linux/i2c-algo-bit.h> 26 27struct simtec_i2c_data { 28 struct resource *ioarea; 29 void __iomem *reg; 30 struct i2c_adapter adap; 31 struct i2c_algo_bit_data bit; 32}; 33 34#define CMD_SET_SDA (1<<2) 35#define CMD_SET_SCL (1<<3) 36 37#define STATE_SDA (1<<0) 38#define STATE_SCL (1<<1) 39 40/* i2c bit-bus functions */ 41 42static void simtec_i2c_setsda(void *pw, int state) 43{ 44 struct simtec_i2c_data *pd = pw; 45 writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg); 46} 47 48static void simtec_i2c_setscl(void *pw, int state) 49{ 50 struct simtec_i2c_data *pd = pw; 51 writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg); 52} 53 54static int simtec_i2c_getsda(void *pw) 55{ 56 struct simtec_i2c_data *pd = pw; 57 return readb(pd->reg) & STATE_SDA ? 1 : 0; 58} 59 60static int simtec_i2c_getscl(void *pw) 61{ 62 struct simtec_i2c_data *pd = pw; 63 return readb(pd->reg) & STATE_SCL ? 1 : 0; 64} 65 66/* device registration */ 67 68static int simtec_i2c_probe(struct platform_device *dev) 69{ 70 struct simtec_i2c_data *pd; 71 struct resource *res; 72 int size; 73 int ret; 74 75 pd = kzalloc(sizeof(struct simtec_i2c_data), GFP_KERNEL); 76 if (pd == NULL) 77 return -ENOMEM; 78 79 platform_set_drvdata(dev, pd); 80 81 res = platform_get_resource(dev, IORESOURCE_MEM, 0); 82 if (res == NULL) { 83 dev_err(&dev->dev, "cannot find IO resource\n"); 84 ret = -ENOENT; 85 goto err; 86 } 87 88 size = resource_size(res); 89 90 pd->ioarea = request_mem_region(res->start, size, dev->name); 91 if (pd->ioarea == NULL) { 92 dev_err(&dev->dev, "cannot request IO\n"); 93 ret = -ENXIO; 94 goto err; 95 } 96 97 pd->reg = ioremap(res->start, size); 98 if (pd->reg == NULL) { 99 dev_err(&dev->dev, "cannot map IO\n"); 100 ret = -ENXIO; 101 goto err_res; 102 } 103 104 /* setup the private data */ 105 106 pd->adap.owner = THIS_MODULE; 107 pd->adap.algo_data = &pd->bit; 108 pd->adap.dev.parent = &dev->dev; 109 110 strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); 111 112 pd->bit.data = pd; 113 pd->bit.setsda = simtec_i2c_setsda; 114 pd->bit.setscl = simtec_i2c_setscl; 115 pd->bit.getsda = simtec_i2c_getsda; 116 pd->bit.getscl = simtec_i2c_getscl; 117 pd->bit.timeout = HZ; 118 pd->bit.udelay = 20; 119 120 ret = i2c_bit_add_bus(&pd->adap); 121 if (ret) 122 goto err_all; 123 124 return 0; 125 126 err_all: 127 iounmap(pd->reg); 128 129 err_res: 130 release_resource(pd->ioarea); 131 kfree(pd->ioarea); 132 133 err: 134 kfree(pd); 135 return ret; 136} 137 138static int simtec_i2c_remove(struct platform_device *dev) 139{ 140 struct simtec_i2c_data *pd = platform_get_drvdata(dev); 141 142 i2c_del_adapter(&pd->adap); 143 144 iounmap(pd->reg); 145 release_resource(pd->ioarea); 146 kfree(pd->ioarea); 147 kfree(pd); 148 149 return 0; 150} 151 152/* device driver */ 153 154static struct platform_driver simtec_i2c_driver = { 155 .driver = { 156 .name = "simtec-i2c", 157 }, 158 .probe = simtec_i2c_probe, 159 .remove = simtec_i2c_remove, 160}; 161 162module_platform_driver(simtec_i2c_driver); 163 164MODULE_DESCRIPTION("Simtec Generic I2C Bus driver"); 165MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 166MODULE_LICENSE("GPL"); 167MODULE_ALIAS("platform:simtec-i2c"); 168