Commit | Line | Data |
---|---|---|
bcda9f1e BD |
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 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/platform_device.h> | |
26 | ||
27 | #include <linux/i2c.h> | |
28 | #include <linux/i2c-algo-bit.h> | |
29 | ||
30 | #include <asm/io.h> | |
31 | ||
32 | struct simtec_i2c_data { | |
33 | struct resource *ioarea; | |
34 | void __iomem *reg; | |
35 | struct i2c_adapter adap; | |
36 | struct i2c_algo_bit_data bit; | |
37 | }; | |
38 | ||
39 | #define CMD_SET_SDA (1<<2) | |
40 | #define CMD_SET_SCL (1<<3) | |
41 | ||
42 | #define STATE_SDA (1<<0) | |
43 | #define STATE_SCL (1<<1) | |
44 | ||
45 | /* i2c bit-bus functions */ | |
46 | ||
47 | static void simtec_i2c_setsda(void *pw, int state) | |
48 | { | |
49 | struct simtec_i2c_data *pd = pw; | |
50 | writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg); | |
51 | } | |
52 | ||
53 | static void simtec_i2c_setscl(void *pw, int state) | |
54 | { | |
55 | struct simtec_i2c_data *pd = pw; | |
56 | writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg); | |
57 | } | |
58 | ||
59 | static int simtec_i2c_getsda(void *pw) | |
60 | { | |
61 | struct simtec_i2c_data *pd = pw; | |
62 | return readb(pd->reg) & STATE_SDA ? 1 : 0; | |
63 | } | |
64 | ||
65 | static int simtec_i2c_getscl(void *pw) | |
66 | { | |
67 | struct simtec_i2c_data *pd = pw; | |
68 | return readb(pd->reg) & STATE_SCL ? 1 : 0; | |
69 | } | |
70 | ||
71 | /* device registration */ | |
72 | ||
73 | static int simtec_i2c_probe(struct platform_device *dev) | |
74 | { | |
75 | struct simtec_i2c_data *pd; | |
76 | struct resource *res; | |
77 | int size; | |
78 | int ret; | |
79 | ||
80 | pd = kzalloc(sizeof(struct simtec_i2c_data), GFP_KERNEL); | |
81 | if (pd == NULL) { | |
82 | dev_err(&dev->dev, "cannot allocate private data\n"); | |
83 | return -ENOMEM; | |
84 | } | |
85 | ||
86 | platform_set_drvdata(dev, pd); | |
87 | ||
88 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
89 | if (res == NULL) { | |
90 | dev_err(&dev->dev, "cannot find IO resource\n"); | |
91 | ret = -ENOENT; | |
92 | goto err; | |
93 | } | |
94 | ||
95 | size = (res->end-res->start)+1; | |
96 | ||
97 | pd->ioarea = request_mem_region(res->start, size, dev->name); | |
98 | if (pd->ioarea == NULL) { | |
99 | dev_err(&dev->dev, "cannot request IO\n"); | |
100 | ret = -ENXIO; | |
101 | goto err; | |
102 | } | |
103 | ||
104 | pd->reg = ioremap(res->start, size); | |
105 | if (pd->reg == NULL) { | |
106 | dev_err(&dev->dev, "cannot map IO\n"); | |
107 | ret = -ENXIO; | |
108 | goto err_res; | |
109 | } | |
110 | ||
111 | /* setup the private data */ | |
112 | ||
113 | pd->adap.owner = THIS_MODULE; | |
114 | pd->adap.algo_data = &pd->bit; | |
115 | pd->adap.dev.parent = &dev->dev; | |
116 | ||
117 | strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); | |
118 | ||
119 | pd->bit.data = pd; | |
120 | pd->bit.setsda = simtec_i2c_setsda; | |
121 | pd->bit.setscl = simtec_i2c_setscl; | |
122 | pd->bit.getsda = simtec_i2c_getsda; | |
123 | pd->bit.getscl = simtec_i2c_getscl; | |
124 | pd->bit.timeout = HZ; | |
125 | pd->bit.udelay = 20; | |
126 | ||
127 | ret = i2c_bit_add_bus(&pd->adap); | |
128 | if (ret) | |
129 | goto err_all; | |
130 | ||
131 | return 0; | |
132 | ||
133 | err_all: | |
134 | iounmap(pd->reg); | |
135 | ||
136 | err_res: | |
137 | release_resource(pd->ioarea); | |
138 | kfree(pd->ioarea); | |
139 | ||
140 | err: | |
141 | kfree(pd); | |
142 | return ret; | |
143 | } | |
144 | ||
145 | static int simtec_i2c_remove(struct platform_device *dev) | |
146 | { | |
147 | struct simtec_i2c_data *pd = platform_get_drvdata(dev); | |
148 | ||
149 | i2c_del_adapter(&pd->adap); | |
150 | ||
151 | iounmap(pd->reg); | |
152 | release_resource(pd->ioarea); | |
153 | kfree(pd->ioarea); | |
154 | kfree(pd); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | ||
160 | /* device driver */ | |
161 | ||
add8eda7 KS |
162 | /* work with hotplug and coldplug */ |
163 | MODULE_ALIAS("platform:simtec-i2c"); | |
164 | ||
bcda9f1e BD |
165 | static struct platform_driver simtec_i2c_driver = { |
166 | .driver = { | |
167 | .name = "simtec-i2c", | |
168 | .owner = THIS_MODULE, | |
169 | }, | |
170 | .probe = simtec_i2c_probe, | |
171 | .remove = simtec_i2c_remove, | |
172 | }; | |
173 | ||
174 | static int __init i2c_adap_simtec_init(void) | |
175 | { | |
176 | return platform_driver_register(&simtec_i2c_driver); | |
177 | } | |
178 | ||
179 | static void __exit i2c_adap_simtec_exit(void) | |
180 | { | |
181 | platform_driver_unregister(&simtec_i2c_driver); | |
182 | } | |
183 | ||
184 | module_init(i2c_adap_simtec_init); | |
185 | module_exit(i2c_adap_simtec_exit); | |
186 | ||
187 | MODULE_DESCRIPTION("Simtec Generic I2C Bus driver"); | |
188 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | |
189 | MODULE_LICENSE("GPL"); |