diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/i2c/busses/i2c-amd8111.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/i2c/busses/i2c-amd8111.c')
-rw-r--r-- | drivers/i2c/busses/i2c-amd8111.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c new file mode 100644 index 000000000000..af22b401a38b --- /dev/null +++ b/drivers/i2c/busses/i2c-amd8111.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* | ||
2 | * SMBus 2.0 driver for AMD-8111 IO-Hub. | ||
3 | * | ||
4 | * Copyright (c) 2002 Vojtech Pavlik | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation version 2. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/stddef.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/ioport.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <asm/io.h> | ||
22 | |||
23 | MODULE_LICENSE("GPL"); | ||
24 | MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>"); | ||
25 | MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); | ||
26 | |||
27 | struct amd_smbus { | ||
28 | struct pci_dev *dev; | ||
29 | struct i2c_adapter adapter; | ||
30 | int base; | ||
31 | int size; | ||
32 | }; | ||
33 | |||
34 | /* | ||
35 | * AMD PCI control registers definitions. | ||
36 | */ | ||
37 | |||
38 | #define AMD_PCI_MISC 0x48 | ||
39 | |||
40 | #define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ | ||
41 | #define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ | ||
42 | #define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ | ||
43 | |||
44 | /* | ||
45 | * ACPI 2.0 chapter 13 PCI interface definitions. | ||
46 | */ | ||
47 | |||
48 | #define AMD_EC_DATA 0x00 /* data register */ | ||
49 | #define AMD_EC_SC 0x04 /* status of controller */ | ||
50 | #define AMD_EC_CMD 0x04 /* command register */ | ||
51 | #define AMD_EC_ICR 0x08 /* interrupt control register */ | ||
52 | |||
53 | #define AMD_EC_SC_SMI 0x04 /* smi event pending */ | ||
54 | #define AMD_EC_SC_SCI 0x02 /* sci event pending */ | ||
55 | #define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ | ||
56 | #define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ | ||
57 | #define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ | ||
58 | #define AMD_EC_SC_OBF 0x01 /* data ready for host */ | ||
59 | |||
60 | #define AMD_EC_CMD_RD 0x80 /* read EC */ | ||
61 | #define AMD_EC_CMD_WR 0x81 /* write EC */ | ||
62 | #define AMD_EC_CMD_BE 0x82 /* enable burst mode */ | ||
63 | #define AMD_EC_CMD_BD 0x83 /* disable burst mode */ | ||
64 | #define AMD_EC_CMD_QR 0x84 /* query EC */ | ||
65 | |||
66 | /* | ||
67 | * ACPI 2.0 chapter 13 access of registers of the EC | ||
68 | */ | ||
69 | |||
70 | static unsigned int amd_ec_wait_write(struct amd_smbus *smbus) | ||
71 | { | ||
72 | int timeout = 500; | ||
73 | |||
74 | while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF)) | ||
75 | udelay(1); | ||
76 | |||
77 | if (!timeout) { | ||
78 | dev_warn(&smbus->dev->dev, "Timeout while waiting for IBF to clear\n"); | ||
79 | return -1; | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static unsigned int amd_ec_wait_read(struct amd_smbus *smbus) | ||
86 | { | ||
87 | int timeout = 500; | ||
88 | |||
89 | while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF)) | ||
90 | udelay(1); | ||
91 | |||
92 | if (!timeout) { | ||
93 | dev_warn(&smbus->dev->dev, "Timeout while waiting for OBF to set\n"); | ||
94 | return -1; | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data) | ||
101 | { | ||
102 | if (amd_ec_wait_write(smbus)) | ||
103 | return -1; | ||
104 | outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); | ||
105 | |||
106 | if (amd_ec_wait_write(smbus)) | ||
107 | return -1; | ||
108 | outb(address, smbus->base + AMD_EC_DATA); | ||
109 | |||
110 | if (amd_ec_wait_read(smbus)) | ||
111 | return -1; | ||
112 | *data = inb(smbus->base + AMD_EC_DATA); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data) | ||
118 | { | ||
119 | if (amd_ec_wait_write(smbus)) | ||
120 | return -1; | ||
121 | outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); | ||
122 | |||
123 | if (amd_ec_wait_write(smbus)) | ||
124 | return -1; | ||
125 | outb(address, smbus->base + AMD_EC_DATA); | ||
126 | |||
127 | if (amd_ec_wait_write(smbus)) | ||
128 | return -1; | ||
129 | outb(data, smbus->base + AMD_EC_DATA); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * ACPI 2.0 chapter 13 SMBus 2.0 EC register model | ||
136 | */ | ||
137 | |||
138 | #define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ | ||
139 | #define AMD_SMB_STS 0x01 /* status */ | ||
140 | #define AMD_SMB_ADDR 0x02 /* address */ | ||
141 | #define AMD_SMB_CMD 0x03 /* command */ | ||
142 | #define AMD_SMB_DATA 0x04 /* 32 data registers */ | ||
143 | #define AMD_SMB_BCNT 0x24 /* number of data bytes */ | ||
144 | #define AMD_SMB_ALRM_A 0x25 /* alarm address */ | ||
145 | #define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ | ||
146 | |||
147 | #define AMD_SMB_STS_DONE 0x80 | ||
148 | #define AMD_SMB_STS_ALRM 0x40 | ||
149 | #define AMD_SMB_STS_RES 0x20 | ||
150 | #define AMD_SMB_STS_STATUS 0x1f | ||
151 | |||
152 | #define AMD_SMB_STATUS_OK 0x00 | ||
153 | #define AMD_SMB_STATUS_FAIL 0x07 | ||
154 | #define AMD_SMB_STATUS_DNAK 0x10 | ||
155 | #define AMD_SMB_STATUS_DERR 0x11 | ||
156 | #define AMD_SMB_STATUS_CMD_DENY 0x12 | ||
157 | #define AMD_SMB_STATUS_UNKNOWN 0x13 | ||
158 | #define AMD_SMB_STATUS_ACC_DENY 0x17 | ||
159 | #define AMD_SMB_STATUS_TIMEOUT 0x18 | ||
160 | #define AMD_SMB_STATUS_NOTSUP 0x19 | ||
161 | #define AMD_SMB_STATUS_BUSY 0x1A | ||
162 | #define AMD_SMB_STATUS_PEC 0x1F | ||
163 | |||
164 | #define AMD_SMB_PRTCL_WRITE 0x00 | ||
165 | #define AMD_SMB_PRTCL_READ 0x01 | ||
166 | #define AMD_SMB_PRTCL_QUICK 0x02 | ||
167 | #define AMD_SMB_PRTCL_BYTE 0x04 | ||
168 | #define AMD_SMB_PRTCL_BYTE_DATA 0x06 | ||
169 | #define AMD_SMB_PRTCL_WORD_DATA 0x08 | ||
170 | #define AMD_SMB_PRTCL_BLOCK_DATA 0x0a | ||
171 | #define AMD_SMB_PRTCL_PROC_CALL 0x0c | ||
172 | #define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d | ||
173 | #define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a | ||
174 | #define AMD_SMB_PRTCL_PEC 0x80 | ||
175 | |||
176 | |||
177 | static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, | ||
178 | char read_write, u8 command, int size, union i2c_smbus_data * data) | ||
179 | { | ||
180 | struct amd_smbus *smbus = adap->algo_data; | ||
181 | unsigned char protocol, len, pec, temp[2]; | ||
182 | int i; | ||
183 | |||
184 | protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE; | ||
185 | pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; | ||
186 | |||
187 | switch (size) { | ||
188 | |||
189 | case I2C_SMBUS_QUICK: | ||
190 | protocol |= AMD_SMB_PRTCL_QUICK; | ||
191 | read_write = I2C_SMBUS_WRITE; | ||
192 | break; | ||
193 | |||
194 | case I2C_SMBUS_BYTE: | ||
195 | if (read_write == I2C_SMBUS_WRITE) | ||
196 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
197 | protocol |= AMD_SMB_PRTCL_BYTE; | ||
198 | break; | ||
199 | |||
200 | case I2C_SMBUS_BYTE_DATA: | ||
201 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
202 | if (read_write == I2C_SMBUS_WRITE) | ||
203 | amd_ec_write(smbus, AMD_SMB_DATA, data->byte); | ||
204 | protocol |= AMD_SMB_PRTCL_BYTE_DATA; | ||
205 | break; | ||
206 | |||
207 | case I2C_SMBUS_WORD_DATA: | ||
208 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
209 | if (read_write == I2C_SMBUS_WRITE) { | ||
210 | amd_ec_write(smbus, AMD_SMB_DATA, data->word); | ||
211 | amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); | ||
212 | } | ||
213 | protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; | ||
214 | break; | ||
215 | |||
216 | case I2C_SMBUS_BLOCK_DATA: | ||
217 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
218 | if (read_write == I2C_SMBUS_WRITE) { | ||
219 | len = min_t(u8, data->block[0], 32); | ||
220 | amd_ec_write(smbus, AMD_SMB_BCNT, len); | ||
221 | for (i = 0; i < len; i++) | ||
222 | amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); | ||
223 | } | ||
224 | protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; | ||
225 | break; | ||
226 | |||
227 | case I2C_SMBUS_I2C_BLOCK_DATA: | ||
228 | len = min_t(u8, data->block[0], 32); | ||
229 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
230 | amd_ec_write(smbus, AMD_SMB_BCNT, len); | ||
231 | if (read_write == I2C_SMBUS_WRITE) | ||
232 | for (i = 0; i < len; i++) | ||
233 | amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); | ||
234 | protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; | ||
235 | break; | ||
236 | |||
237 | case I2C_SMBUS_PROC_CALL: | ||
238 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
239 | amd_ec_write(smbus, AMD_SMB_DATA, data->word); | ||
240 | amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); | ||
241 | protocol = AMD_SMB_PRTCL_PROC_CALL | pec; | ||
242 | read_write = I2C_SMBUS_READ; | ||
243 | break; | ||
244 | |||
245 | case I2C_SMBUS_BLOCK_PROC_CALL: | ||
246 | protocol |= pec; | ||
247 | len = min_t(u8, data->block[0], 31); | ||
248 | amd_ec_write(smbus, AMD_SMB_CMD, command); | ||
249 | amd_ec_write(smbus, AMD_SMB_BCNT, len); | ||
250 | for (i = 0; i < len; i++) | ||
251 | amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); | ||
252 | protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; | ||
253 | read_write = I2C_SMBUS_READ; | ||
254 | break; | ||
255 | |||
256 | case I2C_SMBUS_WORD_DATA_PEC: | ||
257 | case I2C_SMBUS_BLOCK_DATA_PEC: | ||
258 | case I2C_SMBUS_PROC_CALL_PEC: | ||
259 | case I2C_SMBUS_BLOCK_PROC_CALL_PEC: | ||
260 | dev_warn(&adap->dev, "Unexpected software PEC transaction %d\n.", size); | ||
261 | return -1; | ||
262 | |||
263 | default: | ||
264 | dev_warn(&adap->dev, "Unsupported transaction %d\n", size); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); | ||
269 | amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); | ||
270 | |||
271 | amd_ec_read(smbus, AMD_SMB_STS, temp + 0); | ||
272 | |||
273 | if (~temp[0] & AMD_SMB_STS_DONE) { | ||
274 | udelay(500); | ||
275 | amd_ec_read(smbus, AMD_SMB_STS, temp + 0); | ||
276 | } | ||
277 | |||
278 | if (~temp[0] & AMD_SMB_STS_DONE) { | ||
279 | msleep(1); | ||
280 | amd_ec_read(smbus, AMD_SMB_STS, temp + 0); | ||
281 | } | ||
282 | |||
283 | if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) | ||
284 | return -1; | ||
285 | |||
286 | if (read_write == I2C_SMBUS_WRITE) | ||
287 | return 0; | ||
288 | |||
289 | switch (size) { | ||
290 | |||
291 | case I2C_SMBUS_BYTE: | ||
292 | case I2C_SMBUS_BYTE_DATA: | ||
293 | amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); | ||
294 | break; | ||
295 | |||
296 | case I2C_SMBUS_WORD_DATA: | ||
297 | case I2C_SMBUS_PROC_CALL: | ||
298 | amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); | ||
299 | amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); | ||
300 | data->word = (temp[1] << 8) | temp[0]; | ||
301 | break; | ||
302 | |||
303 | case I2C_SMBUS_BLOCK_DATA: | ||
304 | case I2C_SMBUS_BLOCK_PROC_CALL: | ||
305 | amd_ec_read(smbus, AMD_SMB_BCNT, &len); | ||
306 | len = min_t(u8, len, 32); | ||
307 | case I2C_SMBUS_I2C_BLOCK_DATA: | ||
308 | for (i = 0; i < len; i++) | ||
309 | amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1); | ||
310 | data->block[0] = len; | ||
311 | break; | ||
312 | } | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | |||
318 | static u32 amd8111_func(struct i2c_adapter *adapter) | ||
319 | { | ||
320 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | | ||
321 | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | | ||
322 | I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
323 | I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC; | ||
324 | } | ||
325 | |||
326 | static struct i2c_algorithm smbus_algorithm = { | ||
327 | .name = "Non-I2C SMBus 2.0 adapter", | ||
328 | .id = I2C_ALGO_SMBUS, | ||
329 | .smbus_xfer = amd8111_access, | ||
330 | .functionality = amd8111_func, | ||
331 | }; | ||
332 | |||
333 | |||
334 | static struct pci_device_id amd8111_ids[] = { | ||
335 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) }, | ||
336 | { 0, } | ||
337 | }; | ||
338 | |||
339 | MODULE_DEVICE_TABLE (pci, amd8111_ids); | ||
340 | |||
341 | static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
342 | { | ||
343 | struct amd_smbus *smbus; | ||
344 | int error = -ENODEV; | ||
345 | |||
346 | if (~pci_resource_flags(dev, 0) & IORESOURCE_IO) | ||
347 | return -ENODEV; | ||
348 | |||
349 | smbus = kmalloc(sizeof(struct amd_smbus), GFP_KERNEL); | ||
350 | if (!smbus) | ||
351 | return -ENOMEM; | ||
352 | memset(smbus, 0, sizeof(struct amd_smbus)); | ||
353 | |||
354 | smbus->dev = dev; | ||
355 | smbus->base = pci_resource_start(dev, 0); | ||
356 | smbus->size = pci_resource_len(dev, 0); | ||
357 | |||
358 | if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0")) | ||
359 | goto out_kfree; | ||
360 | |||
361 | smbus->adapter.owner = THIS_MODULE; | ||
362 | snprintf(smbus->adapter.name, I2C_NAME_SIZE, | ||
363 | "SMBus2 AMD8111 adapter at %04x", smbus->base); | ||
364 | smbus->adapter.class = I2C_CLASS_HWMON; | ||
365 | smbus->adapter.algo = &smbus_algorithm; | ||
366 | smbus->adapter.algo_data = smbus; | ||
367 | |||
368 | /* set up the driverfs linkage to our parent device */ | ||
369 | smbus->adapter.dev.parent = &dev->dev; | ||
370 | |||
371 | error = i2c_add_adapter(&smbus->adapter); | ||
372 | if (error) | ||
373 | goto out_release_region; | ||
374 | |||
375 | pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); | ||
376 | pci_set_drvdata(dev, smbus); | ||
377 | return 0; | ||
378 | |||
379 | out_release_region: | ||
380 | release_region(smbus->base, smbus->size); | ||
381 | out_kfree: | ||
382 | kfree(smbus); | ||
383 | return -1; | ||
384 | } | ||
385 | |||
386 | |||
387 | static void __devexit amd8111_remove(struct pci_dev *dev) | ||
388 | { | ||
389 | struct amd_smbus *smbus = pci_get_drvdata(dev); | ||
390 | |||
391 | i2c_del_adapter(&smbus->adapter); | ||
392 | release_region(smbus->base, smbus->size); | ||
393 | kfree(smbus); | ||
394 | } | ||
395 | |||
396 | static struct pci_driver amd8111_driver = { | ||
397 | .name = "amd8111_smbus2", | ||
398 | .id_table = amd8111_ids, | ||
399 | .probe = amd8111_probe, | ||
400 | .remove = __devexit_p(amd8111_remove), | ||
401 | }; | ||
402 | |||
403 | static int __init i2c_amd8111_init(void) | ||
404 | { | ||
405 | return pci_register_driver(&amd8111_driver); | ||
406 | } | ||
407 | |||
408 | |||
409 | static void __exit i2c_amd8111_exit(void) | ||
410 | { | ||
411 | pci_unregister_driver(&amd8111_driver); | ||
412 | } | ||
413 | |||
414 | module_init(i2c_amd8111_init); | ||
415 | module_exit(i2c_amd8111_exit); | ||