root/lm-sensors/trunk/kernel/busses/i2c-nforce2.c @ 1936

Revision 1936, 11.2 KB (checked in by mmh, 11 years ago)

Added utility function i2c_delay(1), to replace various "do_pause"
(mmh) functions in bus drivers. Backported from the 2.6 work of Greg KH.

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