diff options
author | Andrew Victor <andrew@sanpeople.com> | 2006-12-10 15:21:30 -0500 |
---|---|---|
committer | Jean Delvare <khali@arrakis.delvare> | 2006-12-10 15:21:30 -0500 |
commit | 813e30e9ab917f5493147451cfdbf6356a3dcee0 (patch) | |
tree | 1d44e7b9bbe33c352e15d16e1e7417789ec593cc /drivers/i2c | |
parent | 6ea23039cb1cc7c379eb5fba0ed2c53291e9bea7 (diff) |
i2c: New Atmel AT91 bus driver
Add support for the I2C (Two-wire interface) controller integrated in
the Atmel AT91RM9200 processor. This driver should also be usable on
the Atmel AT91SAM9261 and AT91SAM9260 processors.
Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 7 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-at91.c | 325 |
3 files changed, 333 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 291da25e84d1..ab398b700586 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
@@ -74,6 +74,13 @@ config I2C_AMD8111 | |||
74 | This driver can also be built as a module. If so, the module | 74 | This driver can also be built as a module. If so, the module |
75 | will be called i2c-amd8111. | 75 | will be called i2c-amd8111. |
76 | 76 | ||
77 | config I2C_AT91 | ||
78 | tristate "Atmel AT91 I2C Two-Wire interface (TWI)" | ||
79 | depends on I2C && ARCH_AT91 && EXPERIMENTAL | ||
80 | help | ||
81 | This supports the use of the I2C interface on Atmel AT91 | ||
82 | processors. | ||
83 | |||
77 | config I2C_AU1550 | 84 | config I2C_AU1550 |
78 | tristate "Au1550/Au1200 SMBus interface" | 85 | tristate "Au1550/Au1200 SMBus interface" |
79 | depends on I2C && (SOC_AU1550 || SOC_AU1200) | 86 | depends on I2C && (SOC_AU1550 || SOC_AU1200) |
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 626ac71af959..a82b6bfad046 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile | |||
@@ -8,6 +8,7 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o | |||
8 | obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o | 8 | obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o |
9 | obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o | 9 | obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o |
10 | obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o | 10 | obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o |
11 | obj-$(CONFIG_I2C_AT91) += i2c-at91.o | ||
11 | obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o | 12 | obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o |
12 | obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o | 13 | obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o |
13 | obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o | 14 | obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o |
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c new file mode 100644 index 000000000000..67f91bdda089 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | i2c Support for Atmel's AT91 Two-Wire Interface (TWI) | ||
3 | |||
4 | Copyright (C) 2004 Rick Bronson | ||
5 | Converted to 2.6 by Andrew Victor <andrew@sanpeople.com> | ||
6 | |||
7 | Borrowed heavily from original work by: | ||
8 | Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com> | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/version.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | |||
28 | #include <asm/io.h> | ||
29 | |||
30 | #include <asm/arch/at91_twi.h> | ||
31 | #include <asm/arch/board.h> | ||
32 | #include <asm/arch/cpu.h> | ||
33 | |||
34 | #define TWI_CLOCK 100000 /* Hz. max 400 Kbits/sec */ | ||
35 | |||
36 | |||
37 | static struct clk *twi_clk; | ||
38 | static void __iomem *twi_base; | ||
39 | |||
40 | #define at91_twi_read(reg) __raw_readl(twi_base + (reg)) | ||
41 | #define at91_twi_write(reg, val) __raw_writel((val), twi_base + (reg)) | ||
42 | |||
43 | |||
44 | /* | ||
45 | * Initialize the TWI hardware registers. | ||
46 | */ | ||
47 | static void __devinit at91_twi_hwinit(void) | ||
48 | { | ||
49 | unsigned long cdiv, ckdiv; | ||
50 | |||
51 | at91_twi_write(AT91_TWI_IDR, 0xffffffff); /* Disable all interrupts */ | ||
52 | at91_twi_write(AT91_TWI_CR, AT91_TWI_SWRST); /* Reset peripheral */ | ||
53 | at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); /* Set Master mode */ | ||
54 | |||
55 | /* Calcuate clock dividers */ | ||
56 | cdiv = (clk_get_rate(twi_clk) / (2 * TWI_CLOCK)) - 3; | ||
57 | cdiv = cdiv + 1; /* round up */ | ||
58 | ckdiv = 0; | ||
59 | while (cdiv > 255) { | ||
60 | ckdiv++; | ||
61 | cdiv = cdiv >> 1; | ||
62 | } | ||
63 | |||
64 | if (cpu_is_at91rm9200()) { /* AT91RM9200 Errata #22 */ | ||
65 | if (ckdiv > 5) { | ||
66 | printk(KERN_ERR "AT91 I2C: Invalid TWI_CLOCK value!\n"); | ||
67 | ckdiv = 5; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | at91_twi_write(AT91_TWI_CWGR, (ckdiv << 16) | (cdiv << 8) | cdiv); | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * Poll the i2c status register until the specified bit is set. | ||
76 | * Returns 0 if timed out (100 msec). | ||
77 | */ | ||
78 | static short at91_poll_status(unsigned long bit) | ||
79 | { | ||
80 | int loop_cntr = 10000; | ||
81 | |||
82 | do { | ||
83 | udelay(10); | ||
84 | } while (!(at91_twi_read(AT91_TWI_SR) & bit) && (--loop_cntr > 0)); | ||
85 | |||
86 | return (loop_cntr > 0); | ||
87 | } | ||
88 | |||
89 | static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) | ||
90 | { | ||
91 | /* Send Start */ | ||
92 | at91_twi_write(AT91_TWI_CR, AT91_TWI_START); | ||
93 | |||
94 | /* Read data */ | ||
95 | while (length--) { | ||
96 | if (!length) /* need to send Stop before reading last byte */ | ||
97 | at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP); | ||
98 | if (!at91_poll_status(AT91_TWI_RXRDY)) { | ||
99 | dev_dbg(&adap->dev, "RXRDY timeout\n"); | ||
100 | return -ETIMEDOUT; | ||
101 | } | ||
102 | *buf++ = (at91_twi_read(AT91_TWI_RHR) & 0xff); | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length) | ||
109 | { | ||
110 | /* Load first byte into transmitter */ | ||
111 | at91_twi_write(AT91_TWI_THR, *buf++); | ||
112 | |||
113 | /* Send Start */ | ||
114 | at91_twi_write(AT91_TWI_CR, AT91_TWI_START); | ||
115 | |||
116 | do { | ||
117 | if (!at91_poll_status(AT91_TWI_TXRDY)) { | ||
118 | dev_dbg(&adap->dev, "TXRDY timeout\n"); | ||
119 | return -ETIMEDOUT; | ||
120 | } | ||
121 | |||
122 | length--; /* byte was transmitted */ | ||
123 | |||
124 | if (length > 0) /* more data to send? */ | ||
125 | at91_twi_write(AT91_TWI_THR, *buf++); | ||
126 | } while (length); | ||
127 | |||
128 | /* Send Stop */ | ||
129 | at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Generic i2c master transfer entrypoint. | ||
136 | * | ||
137 | * Note: We do not use Atmel's feature of storing the "internal device address". | ||
138 | * Instead the "internal device address" has to be written using a seperate | ||
139 | * i2c message. | ||
140 | * http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html | ||
141 | */ | ||
142 | static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) | ||
143 | { | ||
144 | int i, ret; | ||
145 | |||
146 | dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); | ||
147 | |||
148 | for (i = 0; i < num; i++) { | ||
149 | dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, | ||
150 | pmsg->flags & I2C_M_RD ? "read" : "writ", | ||
151 | pmsg->len, pmsg->len > 1 ? "s" : "", | ||
152 | pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); | ||
153 | |||
154 | at91_twi_write(AT91_TWI_MMR, (pmsg->addr << 16) | ||
155 | | ((pmsg->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0)); | ||
156 | |||
157 | if (pmsg->len && pmsg->buf) { /* sanity check */ | ||
158 | if (pmsg->flags & I2C_M_RD) | ||
159 | ret = xfer_read(adap, pmsg->buf, pmsg->len); | ||
160 | else | ||
161 | ret = xfer_write(adap, pmsg->buf, pmsg->len); | ||
162 | |||
163 | if (ret) | ||
164 | return ret; | ||
165 | |||
166 | /* Wait until transfer is finished */ | ||
167 | if (!at91_poll_status(AT91_TWI_TXCOMP)) { | ||
168 | dev_dbg(&adap->dev, "TXCOMP timeout\n"); | ||
169 | return -ETIMEDOUT; | ||
170 | } | ||
171 | } | ||
172 | dev_dbg(&adap->dev, "transfer complete\n"); | ||
173 | pmsg++; /* next message */ | ||
174 | } | ||
175 | return i; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Return list of supported functionality. | ||
180 | */ | ||
181 | static u32 at91_func(struct i2c_adapter *adapter) | ||
182 | { | ||
183 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
184 | } | ||
185 | |||
186 | static struct i2c_algorithm at91_algorithm = { | ||
187 | .master_xfer = at91_xfer, | ||
188 | .functionality = at91_func, | ||
189 | }; | ||
190 | |||
191 | /* | ||
192 | * Main initialization routine. | ||
193 | */ | ||
194 | static int __devinit at91_i2c_probe(struct platform_device *pdev) | ||
195 | { | ||
196 | struct i2c_adapter *adapter; | ||
197 | struct resource *res; | ||
198 | int rc; | ||
199 | |||
200 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
201 | if (!res) | ||
202 | return -ENXIO; | ||
203 | |||
204 | if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c")) | ||
205 | return -EBUSY; | ||
206 | |||
207 | twi_base = ioremap(res->start, res->end - res->start + 1); | ||
208 | if (!twi_base) { | ||
209 | rc = -ENOMEM; | ||
210 | goto fail0; | ||
211 | } | ||
212 | |||
213 | twi_clk = clk_get(NULL, "twi_clk"); | ||
214 | if (IS_ERR(twi_clk)) { | ||
215 | dev_err(&pdev->dev, "no clock defined\n"); | ||
216 | rc = -ENODEV; | ||
217 | goto fail1; | ||
218 | } | ||
219 | |||
220 | adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); | ||
221 | if (adapter == NULL) { | ||
222 | dev_err(&pdev->dev, "can't allocate inteface!\n"); | ||
223 | rc = -ENOMEM; | ||
224 | goto fail2; | ||
225 | } | ||
226 | sprintf(adapter->name, "AT91"); | ||
227 | adapter->algo = &at91_algorithm; | ||
228 | adapter->class = I2C_CLASS_HWMON; | ||
229 | adapter->dev.parent = &pdev->dev; | ||
230 | |||
231 | platform_set_drvdata(pdev, adapter); | ||
232 | |||
233 | clk_enable(twi_clk); /* enable peripheral clock */ | ||
234 | at91_twi_hwinit(); /* initialize TWI controller */ | ||
235 | |||
236 | rc = i2c_add_adapter(adapter); | ||
237 | if (rc) { | ||
238 | dev_err(&pdev->dev, "Adapter %s registration failed\n", | ||
239 | adapter->name); | ||
240 | goto fail3; | ||
241 | } | ||
242 | |||
243 | dev_info(&pdev->dev, "AT91 i2c bus driver.\n"); | ||
244 | return 0; | ||
245 | |||
246 | fail3: | ||
247 | platform_set_drvdata(pdev, NULL); | ||
248 | kfree(adapter); | ||
249 | clk_disable(twi_clk); | ||
250 | fail2: | ||
251 | clk_put(twi_clk); | ||
252 | fail1: | ||
253 | iounmap(twi_base); | ||
254 | fail0: | ||
255 | release_mem_region(res->start, res->end - res->start + 1); | ||
256 | |||
257 | return rc; | ||
258 | } | ||
259 | |||
260 | static int __devexit at91_i2c_remove(struct platform_device *pdev) | ||
261 | { | ||
262 | struct i2c_adapter *adapter = platform_get_drvdata(pdev); | ||
263 | struct resource *res; | ||
264 | int rc; | ||
265 | |||
266 | rc = i2c_del_adapter(adapter); | ||
267 | platform_set_drvdata(pdev, NULL); | ||
268 | |||
269 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
270 | iounmap(twi_base); | ||
271 | release_mem_region(res->start, res->end - res->start + 1); | ||
272 | |||
273 | clk_disable(twi_clk); /* disable peripheral clock */ | ||
274 | clk_put(twi_clk); | ||
275 | |||
276 | return rc; | ||
277 | } | ||
278 | |||
279 | #ifdef CONFIG_PM | ||
280 | |||
281 | /* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */ | ||
282 | |||
283 | static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg) | ||
284 | { | ||
285 | clk_disable(twi_clk); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int at91_i2c_resume(struct platform_device *pdev) | ||
290 | { | ||
291 | return clk_enable(twi_clk); | ||
292 | } | ||
293 | |||
294 | #else | ||
295 | #define at91_i2c_suspend NULL | ||
296 | #define at91_i2c_resume NULL | ||
297 | #endif | ||
298 | |||
299 | static struct platform_driver at91_i2c_driver = { | ||
300 | .probe = at91_i2c_probe, | ||
301 | .remove = __devexit_p(at91_i2c_remove), | ||
302 | .suspend = at91_i2c_suspend, | ||
303 | .resume = at91_i2c_resume, | ||
304 | .driver = { | ||
305 | .name = "at91_i2c", | ||
306 | .owner = THIS_MODULE, | ||
307 | }, | ||
308 | }; | ||
309 | |||
310 | static int __init at91_i2c_init(void) | ||
311 | { | ||
312 | return platform_driver_register(&at91_i2c_driver); | ||
313 | } | ||
314 | |||
315 | static void __exit at91_i2c_exit(void) | ||
316 | { | ||
317 | platform_driver_unregister(&at91_i2c_driver); | ||
318 | } | ||
319 | |||
320 | module_init(at91_i2c_init); | ||
321 | module_exit(at91_i2c_exit); | ||
322 | |||
323 | MODULE_AUTHOR("Rick Bronson"); | ||
324 | MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); | ||
325 | MODULE_LICENSE("GPL"); | ||