Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 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 *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 *mmio; |
||
79 | struct s_i2c_bus i2c_bus[MAX_BUSSES]; |
||
80 | }; |
||
81 | |||
82 | |||
83 | /* |
||
84 | * i2c configuration |
||
85 | */ |
||
86 | #ifndef I2C_HW_B_S3VIA |
||
87 | #define I2C_HW_B_S3VIA 0x18 /* S3VIA ProSavage adapter */ |
||
88 | #endif |
||
89 | |||
90 | /* delays */ |
||
91 | #define CYCLE_DELAY 10 |
||
92 | #define TIMEOUT (HZ / 2) |
||
93 | |||
94 | |||
95 | /* |
||
96 | * S3/VIA 8365/8375 registers |
||
97 | */ |
||
98 | #ifndef PCI_DEVICE_ID_S3_SAVAGE4 |
||
99 | #define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 |
||
100 | #endif |
||
101 | #ifndef PCI_DEVICE_ID_S3_PROSAVAGE8 |
||
102 | #define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 |
||
103 | #endif |
||
104 | |||
105 | #define VGA_CR_IX 0x3d4 |
||
106 | #define VGA_CR_DATA 0x3d5 |
||
107 | |||
108 | #define CR_SERIAL1 0xa0 /* I2C serial communications interface */ |
||
109 | #define MM_SERIAL1 0xff20 |
||
110 | #define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ |
||
111 | |||
112 | /* based on vt8365 documentation */ |
||
113 | #define I2C_ENAB 0x10 |
||
114 | #define I2C_SCL_OUT 0x01 |
||
115 | #define I2C_SDA_OUT 0x02 |
||
116 | #define I2C_SCL_IN 0x04 |
||
117 | #define I2C_SDA_IN 0x08 |
||
118 | |||
119 | #define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX) |
||
120 | #define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA) |
||
121 | #define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA) |
||
122 | |||
123 | |||
124 | /* |
||
125 | * Serial bus line handling |
||
126 | * |
||
127 | * serial communications register as parameter in private data |
||
128 | * |
||
129 | * TODO: locks with other code sections accessing video registers? |
||
130 | */ |
||
131 | static void bit_s3via_setscl(void *bus, int val) |
||
132 | { |
||
133 | struct s_i2c_bus *p = (struct s_i2c_bus *)bus; |
||
134 | unsigned int r; |
||
135 | |||
136 | SET_CR_IX(p, p->i2c_reg); |
||
137 | r = GET_CR_DATA(p); |
||
138 | r |= I2C_ENAB; |
||
139 | if (val) { |
||
140 | r |= I2C_SCL_OUT; |
||
141 | } else { |
||
142 | r &= ~I2C_SCL_OUT; |
||
143 | } |
||
144 | SET_CR_DATA(p, r); |
||
145 | } |
||
146 | |||
147 | static void bit_s3via_setsda(void *bus, int val) |
||
148 | { |
||
149 | struct s_i2c_bus *p = (struct s_i2c_bus *)bus; |
||
150 | unsigned int r; |
||
151 | |||
152 | SET_CR_IX(p, p->i2c_reg); |
||
153 | r = GET_CR_DATA(p); |
||
154 | r |= I2C_ENAB; |
||
155 | if (val) { |
||
156 | r |= I2C_SDA_OUT; |
||
157 | } else { |
||
158 | r &= ~I2C_SDA_OUT; |
||
159 | } |
||
160 | SET_CR_DATA(p, r); |
||
161 | } |
||
162 | |||
163 | static int bit_s3via_getscl(void *bus) |
||
164 | { |
||
165 | struct s_i2c_bus *p = (struct s_i2c_bus *)bus; |
||
166 | |||
167 | SET_CR_IX(p, p->i2c_reg); |
||
168 | return (0 != (GET_CR_DATA(p) & I2C_SCL_IN)); |
||
169 | } |
||
170 | |||
171 | static int bit_s3via_getsda(void *bus) |
||
172 | { |
||
173 | struct s_i2c_bus *p = (struct s_i2c_bus *)bus; |
||
174 | |||
175 | SET_CR_IX(p, p->i2c_reg); |
||
176 | return (0 != (GET_CR_DATA(p) & I2C_SDA_IN)); |
||
177 | } |
||
178 | |||
179 | |||
180 | /* |
||
181 | * adapter initialisation |
||
182 | */ |
||
183 | static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, u8 *mmvga, u32 i2c_reg) |
||
184 | { |
||
185 | int ret; |
||
186 | p->adap.owner = THIS_MODULE; |
||
187 | p->adap.id = I2C_HW_B_S3VIA; |
||
188 | p->adap.algo_data = &p->algo; |
||
189 | p->adap.dev.parent = &dev->dev; |
||
190 | p->algo.setsda = bit_s3via_setsda; |
||
191 | p->algo.setscl = bit_s3via_setscl; |
||
192 | p->algo.getsda = bit_s3via_getsda; |
||
193 | p->algo.getscl = bit_s3via_getscl; |
||
194 | p->algo.udelay = CYCLE_DELAY; |
||
195 | p->algo.mdelay = CYCLE_DELAY; |
||
196 | p->algo.timeout = TIMEOUT; |
||
197 | p->algo.data = p; |
||
198 | p->mmvga = mmvga; |
||
199 | p->i2c_reg = i2c_reg; |
||
200 | |||
201 | ret = i2c_bit_add_bus(&p->adap); |
||
202 | if (ret) { |
||
203 | return ret; |
||
204 | } |
||
205 | |||
206 | p->adap_ok = 1; |
||
207 | return 0; |
||
208 | } |
||
209 | |||
210 | |||
211 | /* |
||
212 | * Cleanup stuff |
||
213 | */ |
||
214 | static void __devexit prosavage_remove(struct pci_dev *dev) |
||
215 | { |
||
216 | struct s_i2c_chip *chip; |
||
217 | int i, ret; |
||
218 | |||
219 | chip = (struct s_i2c_chip *)pci_get_drvdata(dev); |
||
220 | |||
221 | if (!chip) { |
||
222 | return; |
||
223 | } |
||
224 | for (i = MAX_BUSSES - 1; i >= 0; i--) { |
||
225 | if (chip->i2c_bus[i].adap_ok == 0) |
||
226 | continue; |
||
227 | |||
228 | ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap); |
||
229 | if (ret) { |
||
230 | dev_err(&dev->dev, ": %s not removed\n", |
||
231 | chip->i2c_bus[i].adap.name); |
||
232 | } |
||
233 | } |
||
234 | if (chip->mmio) { |
||
235 | iounmap(chip->mmio); |
||
236 | } |
||
237 | kfree(chip); |
||
238 | } |
||
239 | |||
240 | |||
241 | /* |
||
242 | * Detect chip and initialize it |
||
243 | */ |
||
244 | static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id) |
||
245 | { |
||
246 | int ret; |
||
247 | unsigned long base, len; |
||
248 | struct s_i2c_chip *chip; |
||
249 | struct s_i2c_bus *bus; |
||
250 | |||
251 | pci_set_drvdata(dev, kmalloc(sizeof(struct s_i2c_chip), GFP_KERNEL)); |
||
252 | chip = (struct s_i2c_chip *)pci_get_drvdata(dev); |
||
253 | if (chip == NULL) { |
||
254 | return -ENOMEM; |
||
255 | } |
||
256 | |||
257 | memset(chip, 0, sizeof(struct s_i2c_chip)); |
||
258 | |||
259 | base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK; |
||
260 | len = dev->resource[0].end - base + 1; |
||
261 | chip->mmio = ioremap_nocache(base, len); |
||
262 | |||
263 | if (chip->mmio == NULL) { |
||
264 | dev_err(&dev->dev, "ioremap failed\n"); |
||
265 | prosavage_remove(dev); |
||
266 | return -ENODEV; |
||
267 | } |
||
268 | |||
269 | |||
270 | /* |
||
271 | * Chip initialisation |
||
272 | */ |
||
273 | /* Unlock Extended IO Space ??? */ |
||
274 | |||
275 | |||
276 | /* |
||
277 | * i2c bus registration |
||
278 | */ |
||
279 | bus = &chip->i2c_bus[0]; |
||
280 | snprintf(bus->adap.name, sizeof(bus->adap.name), |
||
281 | "ProSavage I2C bus at %02x:%02x.%x", |
||
282 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
||
283 | ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1); |
||
284 | if (ret) { |
||
285 | goto err_adap; |
||
286 | } |
||
287 | /* |
||
288 | * ddc bus registration |
||
289 | */ |
||
290 | bus = &chip->i2c_bus[1]; |
||
291 | snprintf(bus->adap.name, sizeof(bus->adap.name), |
||
292 | "ProSavage DDC bus at %02x:%02x.%x", |
||
293 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
||
294 | ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2); |
||
295 | if (ret) { |
||
296 | goto err_adap; |
||
297 | } |
||
298 | return 0; |
||
299 | err_adap: |
||
300 | dev_err(&dev->dev, ": %s failed\n", bus->adap.name); |
||
301 | prosavage_remove(dev); |
||
302 | return ret; |
||
303 | } |
||
304 | |||
305 | |||
306 | /* |
||
307 | * Data for PCI driver interface |
||
308 | */ |
||
309 | static struct pci_device_id prosavage_pci_tbl[] = { |
||
310 | { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) }, |
||
311 | { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) }, |
||
312 | { 0, }, |
||
313 | }; |
||
314 | |||
315 | static struct pci_driver prosavage_driver = { |
||
316 | .name = "prosavage-smbus", |
||
317 | .id_table = prosavage_pci_tbl, |
||
318 | .probe = prosavage_probe, |
||
319 | .remove = __devexit_p(prosavage_remove), |
||
320 | }; |
||
321 | |||
322 | static int __init i2c_prosavage_init(void) |
||
323 | { |
||
324 | return pci_module_init(&prosavage_driver); |
||
325 | } |
||
326 | |||
327 | static void __exit i2c_prosavage_exit(void) |
||
328 | { |
||
329 | pci_unregister_driver(&prosavage_driver); |
||
330 | } |
||
331 | |||
332 | MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl); |
||
333 | MODULE_AUTHOR("Henk Vergonet"); |
||
334 | MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver"); |
||
335 | MODULE_LICENSE("GPL"); |
||
336 | |||
337 | module_init (i2c_prosavage_init); |
||
338 | module_exit (i2c_prosavage_exit); |