Rev 420 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware |
||
3 | monitoring |
||
4 | Copyright (c) 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>, |
||
5 | Philip Edelbrock <phil@netroedge.com>, |
||
6 | Ralph Metzler <rjkm@thp.uni-koeln.de>, and |
||
7 | Mark D. Studebaker <mdsxyz123@yahoo.com> |
||
8 | |||
9 | Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and |
||
10 | Simon Vogl |
||
11 | |||
12 | This program is free software; you can redistribute it and/or modify |
||
13 | it under the terms of the GNU General Public License as published by |
||
14 | the Free Software Foundation; either version 2 of the License, or |
||
15 | (at your option) any later version. |
||
16 | |||
17 | This program is distributed in the hope that it will be useful, |
||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
20 | GNU General Public License for more details. |
||
21 | |||
22 | You should have received a copy of the GNU General Public License |
||
23 | along with this program; if not, write to the Free Software |
||
24 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
25 | */ |
||
26 | /* |
||
27 | This interfaces to the I810/I815 to provide access to |
||
28 | the DDC Bus and the I2C Bus. |
||
29 | |||
30 | SUPPORTED DEVICES PCI ID |
||
31 | i810AA 7121 |
||
32 | i810AB 7123 |
||
33 | i810E 7125 |
||
34 | i815 1132 |
||
35 | */ |
||
36 | |||
37 | #include <linux/kernel.h> |
||
38 | #include <linux/module.h> |
||
39 | #include <linux/init.h> |
||
40 | #include <linux/pci.h> |
||
41 | #include <linux/i2c.h> |
||
42 | #include <linux/i2c-algo-bit.h> |
||
43 | #include <asm/io.h> |
||
44 | |||
45 | /* GPIO register locations */ |
||
46 | #define I810_IOCONTROL_OFFSET 0x5000 |
||
47 | #define I810_HVSYNC 0x00 /* not used */ |
||
48 | #define I810_GPIOA 0x10 |
||
49 | #define I810_GPIOB 0x14 |
||
50 | |||
51 | /* bit locations in the registers */ |
||
52 | #define SCL_DIR_MASK 0x0001 |
||
53 | #define SCL_DIR 0x0002 |
||
54 | #define SCL_VAL_MASK 0x0004 |
||
55 | #define SCL_VAL_OUT 0x0008 |
||
56 | #define SCL_VAL_IN 0x0010 |
||
57 | #define SDA_DIR_MASK 0x0100 |
||
58 | #define SDA_DIR 0x0200 |
||
59 | #define SDA_VAL_MASK 0x0400 |
||
60 | #define SDA_VAL_OUT 0x0800 |
||
61 | #define SDA_VAL_IN 0x1000 |
||
62 | |||
63 | /* initialization states */ |
||
64 | #define INIT1 0x1 |
||
65 | #define INIT2 0x2 |
||
66 | #define INIT3 0x4 |
||
67 | |||
68 | /* delays */ |
||
69 | #define CYCLE_DELAY 10 |
||
70 | #define TIMEOUT (HZ / 2) |
||
71 | |||
72 | static void *ioaddr; |
||
73 | |||
74 | /* The i810 GPIO registers have individual masks for each bit |
||
75 | so we never have to read before writing. Nice. */ |
||
76 | |||
77 | static void bit_i810i2c_setscl(void *data, int val) |
||
78 | { |
||
79 | writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, |
||
80 | ioaddr + I810_GPIOB); |
||
81 | readl(ioaddr + I810_GPIOB); /* flush posted write */ |
||
82 | } |
||
83 | |||
84 | static void bit_i810i2c_setsda(void *data, int val) |
||
85 | { |
||
86 | writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, |
||
87 | ioaddr + I810_GPIOB); |
||
88 | readl(ioaddr + I810_GPIOB); /* flush posted write */ |
||
89 | } |
||
90 | |||
91 | /* The GPIO pins are open drain, so the pins could always remain outputs. |
||
92 | However, some chip versions don't latch the inputs unless they |
||
93 | are set as inputs. |
||
94 | We rely on the i2c-algo-bit routines to set the pins high before |
||
95 | reading the input from other chips. Following guidance in the 815 |
||
96 | prog. ref. guide, we do a "dummy write" of 0 to the register before |
||
97 | reading which forces the input value to be latched. We presume this |
||
98 | applies to the 810 as well; shouldn't hurt anyway. This is necessary to get |
||
99 | i2c_algo_bit bit_test=1 to pass. */ |
||
100 | |||
101 | static int bit_i810i2c_getscl(void *data) |
||
102 | { |
||
103 | writel(SCL_DIR_MASK, ioaddr + I810_GPIOB); |
||
104 | writel(0, ioaddr + I810_GPIOB); |
||
105 | return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN)); |
||
106 | } |
||
107 | |||
108 | static int bit_i810i2c_getsda(void *data) |
||
109 | { |
||
110 | writel(SDA_DIR_MASK, ioaddr + I810_GPIOB); |
||
111 | writel(0, ioaddr + I810_GPIOB); |
||
112 | return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN)); |
||
113 | } |
||
114 | |||
115 | static void bit_i810ddc_setscl(void *data, int val) |
||
116 | { |
||
117 | writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, |
||
118 | ioaddr + I810_GPIOA); |
||
119 | readl(ioaddr + I810_GPIOA); /* flush posted write */ |
||
120 | } |
||
121 | |||
122 | static void bit_i810ddc_setsda(void *data, int val) |
||
123 | { |
||
124 | writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, |
||
125 | ioaddr + I810_GPIOA); |
||
126 | readl(ioaddr + I810_GPIOA); /* flush posted write */ |
||
127 | } |
||
128 | |||
129 | static int bit_i810ddc_getscl(void *data) |
||
130 | { |
||
131 | writel(SCL_DIR_MASK, ioaddr + I810_GPIOA); |
||
132 | writel(0, ioaddr + I810_GPIOA); |
||
133 | return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN)); |
||
134 | } |
||
135 | |||
136 | static int bit_i810ddc_getsda(void *data) |
||
137 | { |
||
138 | writel(SDA_DIR_MASK, ioaddr + I810_GPIOA); |
||
139 | writel(0, ioaddr + I810_GPIOA); |
||
140 | return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN)); |
||
141 | } |
||
142 | |||
143 | static int config_i810(struct pci_dev *dev) |
||
144 | { |
||
145 | unsigned long cadr; |
||
146 | |||
147 | /* map I810 memory */ |
||
148 | cadr = dev->resource[1].start; |
||
149 | cadr += I810_IOCONTROL_OFFSET; |
||
150 | cadr &= PCI_BASE_ADDRESS_MEM_MASK; |
||
151 | ioaddr = ioremap_nocache(cadr, 0x1000); |
||
152 | if (ioaddr) { |
||
153 | bit_i810i2c_setscl(NULL, 1); |
||
154 | bit_i810i2c_setsda(NULL, 1); |
||
155 | bit_i810ddc_setscl(NULL, 1); |
||
156 | bit_i810ddc_setsda(NULL, 1); |
||
157 | return 0; |
||
158 | } |
||
159 | return -ENODEV; |
||
160 | } |
||
161 | |||
162 | static struct i2c_algo_bit_data i810_i2c_bit_data = { |
||
163 | .setsda = bit_i810i2c_setsda, |
||
164 | .setscl = bit_i810i2c_setscl, |
||
165 | .getsda = bit_i810i2c_getsda, |
||
166 | .getscl = bit_i810i2c_getscl, |
||
167 | .udelay = CYCLE_DELAY, |
||
168 | .mdelay = CYCLE_DELAY, |
||
169 | .timeout = TIMEOUT, |
||
170 | }; |
||
171 | |||
172 | static struct i2c_adapter i810_i2c_adapter = { |
||
173 | .owner = THIS_MODULE, |
||
174 | .name = "I810/I815 I2C Adapter", |
||
175 | .algo_data = &i810_i2c_bit_data, |
||
176 | }; |
||
177 | |||
178 | static struct i2c_algo_bit_data i810_ddc_bit_data = { |
||
179 | .setsda = bit_i810ddc_setsda, |
||
180 | .setscl = bit_i810ddc_setscl, |
||
181 | .getsda = bit_i810ddc_getsda, |
||
182 | .getscl = bit_i810ddc_getscl, |
||
183 | .udelay = CYCLE_DELAY, |
||
184 | .mdelay = CYCLE_DELAY, |
||
185 | .timeout = TIMEOUT, |
||
186 | }; |
||
187 | |||
188 | static struct i2c_adapter i810_ddc_adapter = { |
||
189 | .owner = THIS_MODULE, |
||
190 | .name = "I810/I815 DDC Adapter", |
||
191 | .algo_data = &i810_ddc_bit_data, |
||
192 | }; |
||
193 | |||
194 | static struct pci_device_id i810_ids[] __devinitdata = { |
||
195 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) }, |
||
196 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) }, |
||
197 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) }, |
||
198 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) }, |
||
199 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) }, |
||
200 | { 0, }, |
||
201 | }; |
||
202 | |||
203 | static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id) |
||
204 | { |
||
205 | int retval; |
||
206 | |||
207 | retval = config_i810(dev); |
||
208 | if (retval) |
||
209 | return retval; |
||
210 | dev_info(&dev->dev, "i810/i815 i2c device found.\n"); |
||
211 | |||
212 | /* set up the sysfs linkage to our parent device */ |
||
213 | i810_i2c_adapter.dev.parent = &dev->dev; |
||
214 | i810_ddc_adapter.dev.parent = &dev->dev; |
||
215 | |||
216 | retval = i2c_bit_add_bus(&i810_i2c_adapter); |
||
217 | if (retval) |
||
218 | return retval; |
||
219 | retval = i2c_bit_add_bus(&i810_ddc_adapter); |
||
220 | if (retval) |
||
221 | i2c_bit_del_bus(&i810_i2c_adapter); |
||
222 | return retval; |
||
223 | } |
||
224 | |||
225 | static void __devexit i810_remove(struct pci_dev *dev) |
||
226 | { |
||
227 | i2c_bit_del_bus(&i810_ddc_adapter); |
||
228 | i2c_bit_del_bus(&i810_i2c_adapter); |
||
229 | iounmap(ioaddr); |
||
230 | } |
||
231 | |||
232 | static struct pci_driver i810_driver = { |
||
233 | .name = "i810 smbus", |
||
234 | .id_table = i810_ids, |
||
235 | .probe = i810_probe, |
||
236 | .remove = __devexit_p(i810_remove), |
||
237 | }; |
||
238 | |||
239 | static int __init i2c_i810_init(void) |
||
240 | { |
||
241 | return pci_module_init(&i810_driver); |
||
242 | } |
||
243 | |||
244 | static void __exit i2c_i810_exit(void) |
||
245 | { |
||
246 | pci_unregister_driver(&i810_driver); |
||
247 | } |
||
248 | |||
249 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " |
||
250 | "Philip Edelbrock <phil@netroedge.com>, " |
||
251 | "Ralph Metzler <rjkm@thp.uni-koeln.de>, " |
||
252 | "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); |
||
253 | MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); |
||
254 | MODULE_LICENSE("GPL"); |
||
255 | |||
256 | module_init(i2c_i810_init); |
||
257 | module_exit(i2c_i810_exit); |