Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | SMBus driver for nVidia nForce2 MCP |
||
3 | |||
4 | Ported to 2.5 Patrick Dreker <patrick@dreker.de>, |
||
5 | Copyright (c) 2003 Hans-Frieder Vogt <hfvogt@arcor.de>, |
||
6 | Based on |
||
7 | SMBus 2.0 driver for AMD-8111 IO-Hub |
||
8 | Copyright (c) 2002 Vojtech Pavlik |
||
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 | This program is distributed in the hope that it will be useful, |
||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | GNU General Public License for more details. |
||
19 | |||
20 | You should have received a copy of the GNU General Public License |
||
21 | along with this program; if not, write to the Free Software |
||
22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
23 | */ |
||
24 | |||
25 | /* |
||
26 | SUPPORTED DEVICES PCI ID |
||
27 | nForce2 MCP 0064 |
||
28 | |||
29 | This driver supports the 2 SMBuses that are included in the MCP2 of the |
||
30 | nForce2 chipset. |
||
31 | */ |
||
32 | |||
33 | /* Note: we assume there can only be one nForce2, with two SMBus interfaces */ |
||
34 | |||
35 | #include <linux/module.h> |
||
36 | #include <linux/pci.h> |
||
37 | #include <linux/kernel.h> |
||
38 | #include <linux/stddef.h> |
||
39 | #include <linux/sched.h> |
||
40 | #include <linux/ioport.h> |
||
41 | #include <linux/init.h> |
||
42 | #include <linux/i2c.h> |
||
43 | #include <linux/delay.h> |
||
44 | #include <asm/io.h> |
||
45 | |||
46 | MODULE_LICENSE("GPL"); |
||
47 | MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>"); |
||
48 | MODULE_DESCRIPTION("nForce2 SMBus driver"); |
||
49 | |||
50 | |||
51 | #ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS |
||
52 | #define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 |
||
53 | #endif |
||
54 | |||
55 | |||
56 | struct nforce2_smbus { |
||
57 | struct pci_dev *dev; |
||
58 | struct i2c_adapter adapter; |
||
59 | int base; |
||
60 | int size; |
||
61 | }; |
||
62 | |||
63 | |||
64 | /* |
||
65 | * nVidia nForce2 SMBus control register definitions |
||
66 | */ |
||
67 | #define NFORCE_PCI_SMB1 0x50 |
||
68 | #define NFORCE_PCI_SMB2 0x54 |
||
69 | |||
70 | |||
71 | /* |
||
72 | * ACPI 2.0 chapter 13 SMBus 2.0 EC register model |
||
73 | */ |
||
74 | #define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */ |
||
75 | #define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */ |
||
76 | #define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */ |
||
77 | #define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */ |
||
78 | #define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */ |
||
79 | #define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data bytes */ |
||
80 | #define NVIDIA_SMB_ALRM_A (smbus->base + 0x25) /* alarm address */ |
||
81 | #define NVIDIA_SMB_ALRM_D (smbus->base + 0x26) /* 2 bytes alarm data */ |
||
82 | |||
83 | #define NVIDIA_SMB_STS_DONE 0x80 |
||
84 | #define NVIDIA_SMB_STS_ALRM 0x40 |
||
85 | #define NVIDIA_SMB_STS_RES 0x20 |
||
86 | #define NVIDIA_SMB_STS_STATUS 0x1f |
||
87 | |||
88 | #define NVIDIA_SMB_PRTCL_WRITE 0x00 |
||
89 | #define NVIDIA_SMB_PRTCL_READ 0x01 |
||
90 | #define NVIDIA_SMB_PRTCL_QUICK 0x02 |
||
91 | #define NVIDIA_SMB_PRTCL_BYTE 0x04 |
||
92 | #define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06 |
||
93 | #define NVIDIA_SMB_PRTCL_WORD_DATA 0x08 |
||
94 | #define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a |
||
95 | #define NVIDIA_SMB_PRTCL_PROC_CALL 0x0c |
||
96 | #define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL 0x0d |
||
97 | #define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA 0x4a |
||
98 | #define NVIDIA_SMB_PRTCL_PEC 0x80 |
||
99 | |||
100 | |||
101 | /* Other settings */ |
||
102 | #define MAX_TIMEOUT 256 |
||
103 | |||
104 | |||
105 | |||
106 | static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, |
||
107 | unsigned short flags, char read_write, |
||
108 | u8 command, int size, union i2c_smbus_data *data); |
||
109 | static u32 nforce2_func(struct i2c_adapter *adapter); |
||
110 | |||
111 | |||
112 | static struct i2c_algorithm smbus_algorithm = { |
||
113 | .name = "Non-I2C SMBus adapter", |
||
114 | .id = I2C_ALGO_SMBUS, |
||
115 | .smbus_xfer = nforce2_access, |
||
116 | .functionality = nforce2_func, |
||
117 | }; |
||
118 | |||
119 | static struct i2c_adapter nforce2_adapter = { |
||
120 | .owner = THIS_MODULE, |
||
121 | .class = I2C_ADAP_CLASS_SMBUS, |
||
122 | .algo = &smbus_algorithm, |
||
123 | .name = "unset", |
||
124 | }; |
||
125 | |||
126 | /* Return -1 on error. See smbus.h for more information */ |
||
127 | static s32 nforce2_access(struct i2c_adapter * adap, u16 addr, |
||
128 | unsigned short flags, char read_write, |
||
129 | u8 command, int size, union i2c_smbus_data * data) |
||
130 | { |
||
131 | struct nforce2_smbus *smbus = adap->algo_data; |
||
132 | unsigned char protocol, pec, temp; |
||
133 | unsigned char len = 0; /* to keep the compiler quiet */ |
||
134 | int timeout = 0; |
||
135 | int i; |
||
136 | |||
137 | protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : |
||
138 | NVIDIA_SMB_PRTCL_WRITE; |
||
139 | pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0; |
||
140 | |||
141 | switch (size) { |
||
142 | |||
143 | case I2C_SMBUS_QUICK: |
||
144 | protocol |= NVIDIA_SMB_PRTCL_QUICK; |
||
145 | read_write = I2C_SMBUS_WRITE; |
||
146 | break; |
||
147 | |||
148 | case I2C_SMBUS_BYTE: |
||
149 | if (read_write == I2C_SMBUS_WRITE) |
||
150 | outb_p(command, NVIDIA_SMB_CMD); |
||
151 | protocol |= NVIDIA_SMB_PRTCL_BYTE; |
||
152 | break; |
||
153 | |||
154 | case I2C_SMBUS_BYTE_DATA: |
||
155 | outb_p(command, NVIDIA_SMB_CMD); |
||
156 | if (read_write == I2C_SMBUS_WRITE) |
||
157 | outb_p(data->byte, NVIDIA_SMB_DATA); |
||
158 | protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA; |
||
159 | break; |
||
160 | |||
161 | case I2C_SMBUS_WORD_DATA: |
||
162 | outb_p(command, NVIDIA_SMB_CMD); |
||
163 | if (read_write == I2C_SMBUS_WRITE) { |
||
164 | outb_p(data->word, NVIDIA_SMB_DATA); |
||
165 | outb_p(data->word >> 8, NVIDIA_SMB_DATA+1); |
||
166 | } |
||
167 | protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec; |
||
168 | break; |
||
169 | |||
170 | case I2C_SMBUS_BLOCK_DATA: |
||
171 | outb_p(command, NVIDIA_SMB_CMD); |
||
172 | if (read_write == I2C_SMBUS_WRITE) { |
||
173 | len = min_t(u8, data->block[0], 32); |
||
174 | outb_p(len, NVIDIA_SMB_BCNT); |
||
175 | for (i = 0; i < len; i++) |
||
176 | outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i); |
||
177 | } |
||
178 | protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec; |
||
179 | break; |
||
180 | |||
181 | case I2C_SMBUS_I2C_BLOCK_DATA: |
||
182 | len = min_t(u8, data->block[0], 32); |
||
183 | outb_p(command, NVIDIA_SMB_CMD); |
||
184 | outb_p(len, NVIDIA_SMB_BCNT); |
||
185 | if (read_write == I2C_SMBUS_WRITE) |
||
186 | for (i = 0; i < len; i++) |
||
187 | outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i); |
||
188 | protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA; |
||
189 | break; |
||
190 | |||
191 | case I2C_SMBUS_PROC_CALL: |
||
192 | dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n"); |
||
193 | return -1; |
||
194 | /* |
||
195 | outb_p(command, NVIDIA_SMB_CMD); |
||
196 | outb_p(data->word, NVIDIA_SMB_DATA); |
||
197 | outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1); |
||
198 | protocol = NVIDIA_SMB_PRTCL_PROC_CALL | pec; |
||
199 | read_write = I2C_SMBUS_READ; |
||
200 | break; |
||
201 | */ |
||
202 | |||
203 | case I2C_SMBUS_BLOCK_PROC_CALL: |
||
204 | dev_err(&adap->dev, "I2C_SMBUS_BLOCK_PROC_CALL not supported!\n"); |
||
205 | return -1; |
||
206 | /* |
||
207 | protocol |= pec; |
||
208 | len = min_t(u8, data->block[0], 31); |
||
209 | outb_p(command, NVIDIA_SMB_CMD); |
||
210 | outb_p(len, NVIDIA_SMB_BCNT); |
||
211 | for (i = 0; i < len; i++) |
||
212 | outb_p(data->block[i + 1], NVIDIA_SMB_DATA + i); |
||
213 | protocol = NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL | pec; |
||
214 | read_write = I2C_SMBUS_READ; |
||
215 | break; |
||
216 | */ |
||
217 | |||
218 | case I2C_SMBUS_WORD_DATA_PEC: |
||
219 | case I2C_SMBUS_BLOCK_DATA_PEC: |
||
220 | case I2C_SMBUS_PROC_CALL_PEC: |
||
221 | case I2C_SMBUS_BLOCK_PROC_CALL_PEC: |
||
222 | dev_err(&adap->dev, "Unexpected software PEC transaction %d\n.", size); |
||
223 | return -1; |
||
224 | |||
225 | default: |
||
226 | dev_err(&adap->dev, "Unsupported transaction %d\n", size); |
||
227 | return -1; |
||
228 | } |
||
229 | |||
230 | outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR); |
||
231 | outb_p(protocol, NVIDIA_SMB_PRTCL); |
||
232 | |||
233 | temp = inb_p(NVIDIA_SMB_STS); |
||
234 | |||
235 | #if 0 |
||
236 | do { |
||
237 | i2c_do_pause(1); |
||
238 | temp = inb_p(NVIDIA_SMB_STS); |
||
239 | } while (((temp & NVIDIA_SMB_STS_DONE) == 0) && (timeout++ < MAX_TIMEOUT)); |
||
240 | #endif |
||
241 | if (~temp & NVIDIA_SMB_STS_DONE) { |
||
242 | udelay(500); |
||
243 | temp = inb_p(NVIDIA_SMB_STS); |
||
244 | } |
||
245 | if (~temp & NVIDIA_SMB_STS_DONE) { |
||
246 | current->state = TASK_INTERRUPTIBLE; |
||
247 | schedule_timeout(HZ/100); |
||
248 | temp = inb_p(NVIDIA_SMB_STS); |
||
249 | } |
||
250 | |||
251 | if ((timeout >= MAX_TIMEOUT) || (~temp & NVIDIA_SMB_STS_DONE) |
||
252 | || (temp & NVIDIA_SMB_STS_STATUS)) |
||
253 | return -1; |
||
254 | |||
255 | if (read_write == I2C_SMBUS_WRITE) |
||
256 | return 0; |
||
257 | |||
258 | switch (size) { |
||
259 | |||
260 | case I2C_SMBUS_BYTE: |
||
261 | case I2C_SMBUS_BYTE_DATA: |
||
262 | data->byte = inb_p(NVIDIA_SMB_DATA); |
||
263 | break; |
||
264 | |||
265 | case I2C_SMBUS_WORD_DATA: |
||
266 | /* case I2C_SMBUS_PROC_CALL: not supported */ |
||
267 | data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8); |
||
268 | break; |
||
269 | |||
270 | case I2C_SMBUS_BLOCK_DATA: |
||
271 | /* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */ |
||
272 | len = inb_p(NVIDIA_SMB_BCNT); |
||
273 | len = min_t(u8, len, 32); |
||
274 | case I2C_SMBUS_I2C_BLOCK_DATA: |
||
275 | for (i = 0; i < len; i++) |
||
276 | data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i); |
||
277 | data->block[0] = len; |
||
278 | break; |
||
279 | } |
||
280 | |||
281 | return 0; |
||
282 | } |
||
283 | |||
284 | |||
285 | static u32 nforce2_func(struct i2c_adapter *adapter) |
||
286 | { |
||
287 | /* other functionality might be possible, but is not tested */ |
||
288 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | |
||
289 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* | |
||
290 | I2C_FUNC_SMBUS_BLOCK_DATA */; |
||
291 | } |
||
292 | |||
293 | |||
294 | static struct pci_device_id nforce2_ids[] = { |
||
295 | { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS, |
||
296 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, |
||
297 | { 0 } |
||
298 | }; |
||
299 | |||
300 | |||
301 | static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg, |
||
302 | struct nforce2_smbus *smbus, char *name) |
||
303 | { |
||
304 | u16 iobase; |
||
305 | int error; |
||
306 | |||
307 | if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) { |
||
308 | dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name); |
||
309 | return -1; |
||
310 | } |
||
311 | smbus->dev = dev; |
||
312 | smbus->base = iobase & 0xfffc; |
||
313 | smbus->size = 8; |
||
314 | |||
315 | if (!request_region(smbus->base, smbus->size, "nForce2 SMBus")) { |
||
316 | dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", |
||
317 | smbus->base, smbus->base+smbus->size-1, name); |
||
318 | return -1; |
||
319 | } |
||
320 | smbus->adapter = nforce2_adapter; |
||
321 | smbus->adapter.algo_data = smbus; |
||
322 | smbus->adapter.dev.parent = &dev->dev; |
||
323 | snprintf(smbus->adapter.name, I2C_NAME_SIZE, |
||
324 | "SMBus nForce2 adapter at %04x", smbus->base); |
||
325 | |||
326 | error = i2c_add_adapter(&smbus->adapter); |
||
327 | if (error) { |
||
328 | dev_err(&smbus->adapter.dev, "Failed to register adapter.\n"); |
||
329 | release_region(smbus->base, smbus->size); |
||
330 | return -1; |
||
331 | } |
||
332 | dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", smbus->base); |
||
333 | return 0; |
||
334 | } |
||
335 | |||
336 | |||
337 | static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) |
||
338 | { |
||
339 | struct nforce2_smbus *smbuses; |
||
340 | int res1, res2; |
||
341 | |||
342 | /* we support 2 SMBus adapters */ |
||
343 | if (!(smbuses = (void *)kmalloc(2*sizeof(struct nforce2_smbus), |
||
344 | GFP_KERNEL))) |
||
345 | return -ENOMEM; |
||
346 | memset (smbuses, 0, 2*sizeof(struct nforce2_smbus)); |
||
347 | pci_set_drvdata(dev, smbuses); |
||
348 | |||
349 | /* SMBus adapter 1 */ |
||
350 | res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); |
||
351 | if (res1 < 0) { |
||
352 | dev_err(&dev->dev, "Error probing SMB1.\n"); |
||
353 | smbuses[0].base = 0; /* to have a check value */ |
||
354 | } |
||
355 | res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2"); |
||
356 | if (res2 < 0) { |
||
357 | dev_err(&dev->dev, "Error probing SMB2.\n"); |
||
358 | smbuses[1].base = 0; /* to have a check value */ |
||
359 | } |
||
360 | if ((res1 < 0) && (res2 < 0)) { |
||
361 | /* we did not find even one of the SMBuses, so we give up */ |
||
362 | kfree(smbuses); |
||
363 | return -ENODEV; |
||
364 | } |
||
365 | |||
366 | return 0; |
||
367 | } |
||
368 | |||
369 | |||
370 | static void __devexit nforce2_remove(struct pci_dev *dev) |
||
371 | { |
||
372 | struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev); |
||
373 | |||
374 | if (smbuses[0].base) { |
||
375 | i2c_del_adapter(&smbuses[0].adapter); |
||
376 | release_region(smbuses[0].base, smbuses[0].size); |
||
377 | } |
||
378 | if (smbuses[1].base) { |
||
379 | i2c_del_adapter(&smbuses[1].adapter); |
||
380 | release_region(smbuses[1].base, smbuses[1].size); |
||
381 | } |
||
382 | kfree(smbuses); |
||
383 | } |
||
384 | |||
385 | static struct pci_driver nforce2_driver = { |
||
386 | .name = "nForce2 SMBus", |
||
387 | .id_table = nforce2_ids, |
||
388 | .probe = nforce2_probe, |
||
389 | .remove = __devexit_p(nforce2_remove), |
||
390 | }; |
||
391 | |||
392 | static int __init nforce2_init(void) |
||
393 | { |
||
394 | return pci_module_init(&nforce2_driver); |
||
395 | } |
||
396 | |||
397 | static void __exit nforce2_exit(void) |
||
398 | { |
||
399 | pci_unregister_driver(&nforce2_driver); |
||
400 | } |
||
401 | |||
402 | module_init(nforce2_init); |
||
403 | module_exit(nforce2_exit); |
||
404 |