aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-prosavage.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/i2c/busses/i2c-prosavage.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/i2c/busses/i2c-prosavage.c')
-rw-r--r--drivers/i2c/busses/i2c-prosavage.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c
new file mode 100644
index 000000000000..13d66289933b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-prosavage.c
@@ -0,0 +1,334 @@
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/config.h>
58#include <linux/module.h>
59#include <linux/init.h>
60#include <linux/pci.h>
61#include <linux/i2c.h>
62#include <linux/i2c-algo-bit.h>
63#include <asm/io.h>
64
65/*
66 * driver configuration
67 */
68#define MAX_BUSSES 2
69
70struct s_i2c_bus {
71 void __iomem *mmvga;
72 int i2c_reg;
73 int adap_ok;
74 struct i2c_adapter adap;
75 struct i2c_algo_bit_data algo;
76};
77
78struct s_i2c_chip {
79 void __iomem *mmio;
80 struct s_i2c_bus i2c_bus[MAX_BUSSES];
81};
82
83
84/*
85 * i2c configuration
86 */
87#ifndef I2C_HW_B_S3VIA
88#define I2C_HW_B_S3VIA 0x18 /* S3VIA ProSavage adapter */
89#endif
90
91/* delays */
92#define CYCLE_DELAY 10
93#define TIMEOUT (HZ / 2)
94
95
96/*
97 * S3/VIA 8365/8375 registers
98 */
99#define VGA_CR_IX 0x3d4
100#define VGA_CR_DATA 0x3d5
101
102#define CR_SERIAL1 0xa0 /* I2C serial communications interface */
103#define MM_SERIAL1 0xff20
104#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */
105
106/* based on vt8365 documentation */
107#define I2C_ENAB 0x10
108#define I2C_SCL_OUT 0x01
109#define I2C_SDA_OUT 0x02
110#define I2C_SCL_IN 0x04
111#define I2C_SDA_IN 0x08
112
113#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX)
114#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA)
115#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA)
116
117
118/*
119 * Serial bus line handling
120 *
121 * serial communications register as parameter in private data
122 *
123 * TODO: locks with other code sections accessing video registers?
124 */
125static void bit_s3via_setscl(void *bus, int val)
126{
127 struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
128 unsigned int r;
129
130 SET_CR_IX(p, p->i2c_reg);
131 r = GET_CR_DATA(p);
132 r |= I2C_ENAB;
133 if (val) {
134 r |= I2C_SCL_OUT;
135 } else {
136 r &= ~I2C_SCL_OUT;
137 }
138 SET_CR_DATA(p, r);
139}
140
141static void bit_s3via_setsda(void *bus, int val)
142{
143 struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
144 unsigned int r;
145
146 SET_CR_IX(p, p->i2c_reg);
147 r = GET_CR_DATA(p);
148 r |= I2C_ENAB;
149 if (val) {
150 r |= I2C_SDA_OUT;
151 } else {
152 r &= ~I2C_SDA_OUT;
153 }
154 SET_CR_DATA(p, r);
155}
156
157static int bit_s3via_getscl(void *bus)
158{
159 struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
160
161 SET_CR_IX(p, p->i2c_reg);
162 return (0 != (GET_CR_DATA(p) & I2C_SCL_IN));
163}
164
165static int bit_s3via_getsda(void *bus)
166{
167 struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
168
169 SET_CR_IX(p, p->i2c_reg);
170 return (0 != (GET_CR_DATA(p) & I2C_SDA_IN));
171}
172
173
174/*
175 * adapter initialisation
176 */
177static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg)
178{
179 int ret;
180 p->adap.owner = THIS_MODULE;
181 p->adap.id = I2C_HW_B_S3VIA;
182 p->adap.algo_data = &p->algo;
183 p->adap.dev.parent = &dev->dev;
184 p->algo.setsda = bit_s3via_setsda;
185 p->algo.setscl = bit_s3via_setscl;
186 p->algo.getsda = bit_s3via_getsda;
187 p->algo.getscl = bit_s3via_getscl;
188 p->algo.udelay = CYCLE_DELAY;
189 p->algo.mdelay = CYCLE_DELAY;
190 p->algo.timeout = TIMEOUT;
191 p->algo.data = p;
192 p->mmvga = mmvga;
193 p->i2c_reg = i2c_reg;
194
195 ret = i2c_bit_add_bus(&p->adap);
196 if (ret) {
197 return ret;
198 }
199
200 p->adap_ok = 1;
201 return 0;
202}
203
204
205/*
206 * Cleanup stuff
207 */
208static void prosavage_remove(struct pci_dev *dev)
209{
210 struct s_i2c_chip *chip;
211 int i, ret;
212
213 chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
214
215 if (!chip) {
216 return;
217 }
218 for (i = MAX_BUSSES - 1; i >= 0; i--) {
219 if (chip->i2c_bus[i].adap_ok == 0)
220 continue;
221
222 ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap);
223 if (ret) {
224 dev_err(&dev->dev, "%s not removed\n",
225 chip->i2c_bus[i].adap.name);
226 }
227 }
228 if (chip->mmio) {
229 iounmap(chip->mmio);
230 }
231 kfree(chip);
232}
233
234
235/*
236 * Detect chip and initialize it
237 */
238static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id)
239{
240 int ret;
241 unsigned long base, len;
242 struct s_i2c_chip *chip;
243 struct s_i2c_bus *bus;
244
245 pci_set_drvdata(dev, kmalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
246 chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
247 if (chip == NULL) {
248 return -ENOMEM;
249 }
250
251 memset(chip, 0, sizeof(struct s_i2c_chip));
252
253 base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK;
254 len = dev->resource[0].end - base + 1;
255 chip->mmio = ioremap_nocache(base, len);
256
257 if (chip->mmio == NULL) {
258 dev_err(&dev->dev, "ioremap failed\n");
259 prosavage_remove(dev);
260 return -ENODEV;
261 }
262
263
264 /*
265 * Chip initialisation
266 */
267 /* Unlock Extended IO Space ??? */
268
269
270 /*
271 * i2c bus registration
272 */
273 bus = &chip->i2c_bus[0];
274 snprintf(bus->adap.name, sizeof(bus->adap.name),
275 "ProSavage I2C bus at %02x:%02x.%x",
276 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
277 ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1);
278 if (ret) {
279 goto err_adap;
280 }
281 /*
282 * ddc bus registration
283 */
284 bus = &chip->i2c_bus[1];
285 snprintf(bus->adap.name, sizeof(bus->adap.name),
286 "ProSavage DDC bus at %02x:%02x.%x",
287 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
288 ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2);
289 if (ret) {
290 goto err_adap;
291 }
292 return 0;
293err_adap:
294 dev_err(&dev->dev, "%s failed\n", bus->adap.name);
295 prosavage_remove(dev);
296 return ret;
297}
298
299
300/*
301 * Data for PCI driver interface
302 */
303static struct pci_device_id prosavage_pci_tbl[] = {
304 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) },
305 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) },
306 { 0, },
307};
308
309MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl);
310
311static struct pci_driver prosavage_driver = {
312 .name = "prosavage_smbus",
313 .id_table = prosavage_pci_tbl,
314 .probe = prosavage_probe,
315 .remove = prosavage_remove,
316};
317
318static int __init i2c_prosavage_init(void)
319{
320 return pci_register_driver(&prosavage_driver);
321}
322
323static void __exit i2c_prosavage_exit(void)
324{
325 pci_unregister_driver(&prosavage_driver);
326}
327
328MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl);
329MODULE_AUTHOR("Henk Vergonet");
330MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver");
331MODULE_LICENSE("GPL");
332
333module_init (i2c_prosavage_init);
334module_exit (i2c_prosavage_exit);