[PATCH] i2c: SMBus PEC support rewrite, 3 of 3
[linux-2.6] / drivers / i2c / busses / i2c-prosavage.c
1 /*
2  *    kernel/busses/i2c-prosavage.c
3  *
4  *    i2c bus driver for S3/VIA 8365/8375 graphics processor.
5  *    Copyright (c) 2003 Henk Vergonet <henk@god.dyndns.org>
6  *    Based on code written by:
7  *      Frodo Looijaard <frodol@dds.nl>,
8  *      Philip Edelbrock <phil@netroedge.com>,
9  *      Ralph Metzler <rjkm@thp.uni-koeln.de>, and
10  *      Mark D. Studebaker <mdsxyz123@yahoo.com>
11  *      Simon Vogl
12  *      and others
13  *
14  *    Please read the lm_sensors documentation for details on use.
15  *
16  *    This program is free software; you can redistribute it and/or modify
17  *    it under the terms of the GNU General Public License as published by
18  *    the Free Software Foundation; either version 2 of the License, or
19  *    (at your option) any later version.
20  *
21  *    This program is distributed in the hope that it will be useful,
22  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *    GNU General Public License for more details.
25  *
26  *    You should have received a copy of the GNU General Public License
27  *    along with this program; if not, write to the Free Software
28  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29  *
30  */
31 /*  18-05-2003 HVE - created
32  *  14-06-2003 HVE - adapted for lm_sensors2
33  *  17-06-2003 HVE - linux 2.5.xx compatible
34  *  18-06-2003 HVE - codingstyle
35  *  21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx
36  *                   codingstyle, mmio enabled
37  *
38  *  This driver interfaces to the I2C bus of the VIA north bridge embedded
39  *  ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips.
40  *
41  *  Graphics cores:
42  *   S3/VIA KM266/VT8375 aka ProSavage8
43  *   S3/VIA KM133/VT8365 aka Savage4
44  *
45  *  Two serial busses are implemented:
46  *   SERIAL1 - I2C serial communications interface
47  *   SERIAL2 - DDC2 monitor communications interface
48  *
49  *  Tested on a FX41 mainboard, see http://www.shuttle.com
50  * 
51  *
52  *  TODO:
53  *  - integration with prosavage framebuffer device
54  *    (Additional documentation needed :(
55  */
56
57 #include <linux/module.h>
58 #include <linux/init.h>
59 #include <linux/pci.h>
60 #include <linux/i2c.h>
61 #include <linux/i2c-algo-bit.h>
62 #include <asm/io.h>
63
64 /*
65  * driver configuration
66  */
67 #define MAX_BUSSES      2
68
69 struct s_i2c_bus {
70         void __iomem *mmvga;
71         int     i2c_reg;
72         int     adap_ok;
73         struct i2c_adapter              adap;
74         struct i2c_algo_bit_data        algo;
75 };
76
77 struct s_i2c_chip {
78         void __iomem *mmio;
79         struct s_i2c_bus        i2c_bus[MAX_BUSSES];
80 };
81
82
83 /*
84  * i2c configuration
85  */
86 #define CYCLE_DELAY     10
87 #define TIMEOUT         (HZ / 2)
88
89
90 /* 
91  * S3/VIA 8365/8375 registers
92  */
93 #define VGA_CR_IX       0x3d4
94 #define VGA_CR_DATA     0x3d5
95
96 #define CR_SERIAL1      0xa0    /* I2C serial communications interface */
97 #define MM_SERIAL1      0xff20
98 #define CR_SERIAL2      0xb1    /* DDC2 monitor communications interface */
99
100 /* based on vt8365 documentation */
101 #define I2C_ENAB        0x10
102 #define I2C_SCL_OUT     0x01
103 #define I2C_SDA_OUT     0x02
104 #define I2C_SCL_IN      0x04
105 #define I2C_SDA_IN      0x08
106
107 #define SET_CR_IX(p, val)       writeb((val), (p)->mmvga + VGA_CR_IX)
108 #define SET_CR_DATA(p, val)     writeb((val), (p)->mmvga + VGA_CR_DATA)
109 #define GET_CR_DATA(p)          readb((p)->mmvga + VGA_CR_DATA)
110
111
112 /*
113  * Serial bus line handling
114  *
115  * serial communications register as parameter in private data
116  *
117  * TODO: locks with other code sections accessing video registers?
118  */
119 static void bit_s3via_setscl(void *bus, int val)
120 {
121         struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
122         unsigned int r;
123
124         SET_CR_IX(p, p->i2c_reg);
125         r = GET_CR_DATA(p);
126         r |= I2C_ENAB;
127         if (val) {
128                 r |= I2C_SCL_OUT;
129         } else {
130                 r &= ~I2C_SCL_OUT;
131         }
132         SET_CR_DATA(p, r);
133 }
134
135 static void bit_s3via_setsda(void *bus, int val)
136 {
137         struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
138         unsigned int r;
139         
140         SET_CR_IX(p, p->i2c_reg);
141         r = GET_CR_DATA(p);
142         r |= I2C_ENAB;
143         if (val) {
144                 r |= I2C_SDA_OUT;
145         } else {
146                 r &= ~I2C_SDA_OUT;
147         }
148         SET_CR_DATA(p, r);
149 }
150
151 static int bit_s3via_getscl(void *bus)
152 {
153         struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
154
155         SET_CR_IX(p, p->i2c_reg);
156         return (0 != (GET_CR_DATA(p) & I2C_SCL_IN));
157 }
158
159 static int bit_s3via_getsda(void *bus)
160 {
161         struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
162
163         SET_CR_IX(p, p->i2c_reg);
164         return (0 != (GET_CR_DATA(p) & I2C_SDA_IN));
165 }
166
167
168 /*
169  * adapter initialisation
170  */
171 static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg)
172 {
173         int ret;
174         p->adap.owner     = THIS_MODULE;
175         p->adap.id        = I2C_HW_B_S3VIA;
176         p->adap.algo_data = &p->algo;
177         p->adap.dev.parent = &dev->dev;
178         p->algo.setsda    = bit_s3via_setsda;
179         p->algo.setscl    = bit_s3via_setscl;
180         p->algo.getsda    = bit_s3via_getsda;
181         p->algo.getscl    = bit_s3via_getscl;
182         p->algo.udelay    = CYCLE_DELAY;
183         p->algo.mdelay    = CYCLE_DELAY;
184         p->algo.timeout   = TIMEOUT;
185         p->algo.data      = p;
186         p->mmvga          = mmvga;
187         p->i2c_reg        = i2c_reg;
188     
189         ret = i2c_bit_add_bus(&p->adap);
190         if (ret) {
191                 return ret;
192         }
193
194         p->adap_ok = 1;
195         return 0;
196 }
197
198
199 /*
200  * Cleanup stuff
201  */
202 static void prosavage_remove(struct pci_dev *dev)
203 {
204         struct s_i2c_chip *chip;
205         int i, ret;
206
207         chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
208
209         if (!chip) {
210                 return;
211         }
212         for (i = MAX_BUSSES - 1; i >= 0; i--) {
213                 if (chip->i2c_bus[i].adap_ok == 0)
214                         continue;
215
216                 ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap);
217                 if (ret) {
218                         dev_err(&dev->dev, "%s not removed\n",
219                                 chip->i2c_bus[i].adap.name);
220                 }
221         }
222         if (chip->mmio) {
223                 iounmap(chip->mmio);
224         }
225         kfree(chip);
226 }
227
228
229 /*
230  * Detect chip and initialize it
231  */
232 static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id)
233 {
234         int ret;
235         unsigned long base, len;
236         struct s_i2c_chip *chip;
237         struct s_i2c_bus  *bus;
238
239         pci_set_drvdata(dev, kzalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
240         chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
241         if (chip == NULL) {
242                 return -ENOMEM;
243         }
244
245         base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK;
246         len  = dev->resource[0].end - base + 1;
247         chip->mmio = ioremap_nocache(base, len);
248
249         if (chip->mmio == NULL) {
250                 dev_err(&dev->dev, "ioremap failed\n");
251                 prosavage_remove(dev);
252                 return -ENODEV;
253         }
254
255
256         /*
257          * Chip initialisation
258          */
259         /* Unlock Extended IO Space ??? */
260
261
262         /*
263          * i2c bus registration
264          */
265         bus = &chip->i2c_bus[0];
266         snprintf(bus->adap.name, sizeof(bus->adap.name),
267                 "ProSavage I2C bus at %02x:%02x.%x",
268                 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
269         ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1);
270         if (ret) {
271                 goto err_adap;
272         }
273         /*
274          * ddc bus registration
275          */
276         bus = &chip->i2c_bus[1];
277         snprintf(bus->adap.name, sizeof(bus->adap.name),
278                 "ProSavage DDC bus at %02x:%02x.%x",
279                 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
280         ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2);
281         if (ret) {
282                 goto err_adap;
283         }
284         return 0;
285 err_adap:
286         dev_err(&dev->dev, "%s failed\n", bus->adap.name);
287         prosavage_remove(dev);
288         return ret;
289 }
290
291
292 /*
293  * Data for PCI driver interface
294  */
295 static struct pci_device_id prosavage_pci_tbl[] = {
296         { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) },
297         { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) },
298         { 0, },
299 };
300
301 MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl);
302
303 static struct pci_driver prosavage_driver = {
304         .owner          =       THIS_MODULE,
305         .name           =       "prosavage_smbus",
306         .id_table       =       prosavage_pci_tbl,
307         .probe          =       prosavage_probe,
308         .remove         =       prosavage_remove,
309 };
310
311 static int __init i2c_prosavage_init(void)
312 {
313         return pci_register_driver(&prosavage_driver);
314 }
315
316 static void __exit i2c_prosavage_exit(void)
317 {
318         pci_unregister_driver(&prosavage_driver);
319 }
320
321 MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl);
322 MODULE_AUTHOR("Henk Vergonet");
323 MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver");
324 MODULE_LICENSE("GPL");
325
326 module_init (i2c_prosavage_init);
327 module_exit (i2c_prosavage_exit);