Pull platform-drivers into test branch
[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.timeout   = TIMEOUT;
184         p->algo.data      = p;
185         p->mmvga          = mmvga;
186         p->i2c_reg        = i2c_reg;
187     
188         ret = i2c_bit_add_bus(&p->adap);
189         if (ret) {
190                 return ret;
191         }
192
193         p->adap_ok = 1;
194         return 0;
195 }
196
197
198 /*
199  * Cleanup stuff
200  */
201 static void prosavage_remove(struct pci_dev *dev)
202 {
203         struct s_i2c_chip *chip;
204         int i, ret;
205
206         chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
207
208         if (!chip) {
209                 return;
210         }
211         for (i = MAX_BUSSES - 1; i >= 0; i--) {
212                 if (chip->i2c_bus[i].adap_ok == 0)
213                         continue;
214
215                 ret = i2c_del_adapter(&chip->i2c_bus[i].adap);
216                 if (ret) {
217                         dev_err(&dev->dev, "%s not removed\n",
218                                 chip->i2c_bus[i].adap.name);
219                 }
220         }
221         if (chip->mmio) {
222                 iounmap(chip->mmio);
223         }
224         kfree(chip);
225 }
226
227
228 /*
229  * Detect chip and initialize it
230  */
231 static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id)
232 {
233         int ret;
234         unsigned long base, len;
235         struct s_i2c_chip *chip;
236         struct s_i2c_bus  *bus;
237
238         pci_set_drvdata(dev, kzalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
239         chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
240         if (chip == NULL) {
241                 return -ENOMEM;
242         }
243
244         base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK;
245         len  = dev->resource[0].end - base + 1;
246         chip->mmio = ioremap_nocache(base, len);
247
248         if (chip->mmio == NULL) {
249                 dev_err(&dev->dev, "ioremap failed\n");
250                 prosavage_remove(dev);
251                 return -ENODEV;
252         }
253
254
255         /*
256          * Chip initialisation
257          */
258         /* Unlock Extended IO Space ??? */
259
260
261         /*
262          * i2c bus registration
263          */
264         bus = &chip->i2c_bus[0];
265         snprintf(bus->adap.name, sizeof(bus->adap.name),
266                 "ProSavage I2C bus at %02x:%02x.%x",
267                 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
268         ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1);
269         if (ret) {
270                 goto err_adap;
271         }
272         /*
273          * ddc bus registration
274          */
275         bus = &chip->i2c_bus[1];
276         snprintf(bus->adap.name, sizeof(bus->adap.name),
277                 "ProSavage DDC bus at %02x:%02x.%x",
278                 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
279         ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2);
280         if (ret) {
281                 goto err_adap;
282         }
283         return 0;
284 err_adap:
285         dev_err(&dev->dev, "%s failed\n", bus->adap.name);
286         prosavage_remove(dev);
287         return ret;
288 }
289
290
291 /*
292  * Data for PCI driver interface
293  */
294 static struct pci_device_id prosavage_pci_tbl[] = {
295         { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) },
296         { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) },
297         { 0, },
298 };
299
300 MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl);
301
302 static struct pci_driver prosavage_driver = {
303         .name           =       "prosavage_smbus",
304         .id_table       =       prosavage_pci_tbl,
305         .probe          =       prosavage_probe,
306         .remove         =       prosavage_remove,
307 };
308
309 static int __init i2c_prosavage_init(void)
310 {
311         return pci_register_driver(&prosavage_driver);
312 }
313
314 static void __exit i2c_prosavage_exit(void)
315 {
316         pci_unregister_driver(&prosavage_driver);
317 }
318
319 MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl);
320 MODULE_AUTHOR("Henk Vergonet");
321 MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver");
322 MODULE_LICENSE("GPL");
323
324 module_init (i2c_prosavage_init);
325 module_exit (i2c_prosavage_exit);