Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | |
142078f6 | 35 | i845G 2562 |
1da177e4 LT |
36 | */ |
37 | ||
1da177e4 LT |
38 | #include <linux/kernel.h> |
39 | #include <linux/module.h> | |
40 | #include <linux/init.h> | |
41 | #include <linux/pci.h> | |
42 | #include <linux/i2c.h> | |
43 | #include <linux/i2c-algo-bit.h> | |
44 | #include <asm/io.h> | |
45 | ||
46 | /* GPIO register locations */ | |
47 | #define I810_IOCONTROL_OFFSET 0x5000 | |
48 | #define I810_HVSYNC 0x00 /* not used */ | |
49 | #define I810_GPIOA 0x10 | |
50 | #define I810_GPIOB 0x14 | |
51 | ||
52 | /* bit locations in the registers */ | |
53 | #define SCL_DIR_MASK 0x0001 | |
54 | #define SCL_DIR 0x0002 | |
55 | #define SCL_VAL_MASK 0x0004 | |
56 | #define SCL_VAL_OUT 0x0008 | |
57 | #define SCL_VAL_IN 0x0010 | |
58 | #define SDA_DIR_MASK 0x0100 | |
59 | #define SDA_DIR 0x0200 | |
60 | #define SDA_VAL_MASK 0x0400 | |
61 | #define SDA_VAL_OUT 0x0800 | |
62 | #define SDA_VAL_IN 0x1000 | |
63 | ||
64 | /* initialization states */ | |
65 | #define INIT1 0x1 | |
66 | #define INIT2 0x2 | |
67 | #define INIT3 0x4 | |
68 | ||
69 | /* delays */ | |
70 | #define CYCLE_DELAY 10 | |
71 | #define TIMEOUT (HZ / 2) | |
72 | ||
73 | static void __iomem *ioaddr; | |
74 | ||
75 | /* The i810 GPIO registers have individual masks for each bit | |
76 | so we never have to read before writing. Nice. */ | |
77 | ||
78 | static void bit_i810i2c_setscl(void *data, int val) | |
79 | { | |
80 | writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, | |
81 | ioaddr + I810_GPIOB); | |
82 | readl(ioaddr + I810_GPIOB); /* flush posted write */ | |
83 | } | |
84 | ||
85 | static void bit_i810i2c_setsda(void *data, int val) | |
86 | { | |
87 | writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, | |
88 | ioaddr + I810_GPIOB); | |
89 | readl(ioaddr + I810_GPIOB); /* flush posted write */ | |
90 | } | |
91 | ||
92 | /* The GPIO pins are open drain, so the pins could always remain outputs. | |
93 | However, some chip versions don't latch the inputs unless they | |
94 | are set as inputs. | |
95 | We rely on the i2c-algo-bit routines to set the pins high before | |
96 | reading the input from other chips. Following guidance in the 815 | |
97 | prog. ref. guide, we do a "dummy write" of 0 to the register before | |
98 | reading which forces the input value to be latched. We presume this | |
99 | applies to the 810 as well; shouldn't hurt anyway. This is necessary to get | |
100 | i2c_algo_bit bit_test=1 to pass. */ | |
101 | ||
102 | static int bit_i810i2c_getscl(void *data) | |
103 | { | |
104 | writel(SCL_DIR_MASK, ioaddr + I810_GPIOB); | |
105 | writel(0, ioaddr + I810_GPIOB); | |
106 | return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN)); | |
107 | } | |
108 | ||
109 | static int bit_i810i2c_getsda(void *data) | |
110 | { | |
111 | writel(SDA_DIR_MASK, ioaddr + I810_GPIOB); | |
112 | writel(0, ioaddr + I810_GPIOB); | |
113 | return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN)); | |
114 | } | |
115 | ||
116 | static void bit_i810ddc_setscl(void *data, int val) | |
117 | { | |
118 | writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, | |
119 | ioaddr + I810_GPIOA); | |
120 | readl(ioaddr + I810_GPIOA); /* flush posted write */ | |
121 | } | |
122 | ||
123 | static void bit_i810ddc_setsda(void *data, int val) | |
124 | { | |
125 | writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, | |
126 | ioaddr + I810_GPIOA); | |
127 | readl(ioaddr + I810_GPIOA); /* flush posted write */ | |
128 | } | |
129 | ||
130 | static int bit_i810ddc_getscl(void *data) | |
131 | { | |
132 | writel(SCL_DIR_MASK, ioaddr + I810_GPIOA); | |
133 | writel(0, ioaddr + I810_GPIOA); | |
134 | return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN)); | |
135 | } | |
136 | ||
137 | static int bit_i810ddc_getsda(void *data) | |
138 | { | |
139 | writel(SDA_DIR_MASK, ioaddr + I810_GPIOA); | |
140 | writel(0, ioaddr + I810_GPIOA); | |
141 | return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN)); | |
142 | } | |
143 | ||
144 | static int config_i810(struct pci_dev *dev) | |
145 | { | |
146 | unsigned long cadr; | |
147 | ||
148 | /* map I810 memory */ | |
149 | cadr = dev->resource[1].start; | |
150 | cadr += I810_IOCONTROL_OFFSET; | |
151 | cadr &= PCI_BASE_ADDRESS_MEM_MASK; | |
152 | ioaddr = ioremap_nocache(cadr, 0x1000); | |
153 | if (ioaddr) { | |
154 | bit_i810i2c_setscl(NULL, 1); | |
155 | bit_i810i2c_setsda(NULL, 1); | |
156 | bit_i810ddc_setscl(NULL, 1); | |
157 | bit_i810ddc_setsda(NULL, 1); | |
158 | return 0; | |
159 | } | |
160 | return -ENODEV; | |
161 | } | |
162 | ||
163 | static struct i2c_algo_bit_data i810_i2c_bit_data = { | |
164 | .setsda = bit_i810i2c_setsda, | |
165 | .setscl = bit_i810i2c_setscl, | |
166 | .getsda = bit_i810i2c_getsda, | |
167 | .getscl = bit_i810i2c_getscl, | |
168 | .udelay = CYCLE_DELAY, | |
1da177e4 LT |
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, | |
1da177e4 LT |
184 | .timeout = TIMEOUT, |
185 | }; | |
186 | ||
187 | static struct i2c_adapter i810_ddc_adapter = { | |
188 | .owner = THIS_MODULE, | |
189 | .name = "I810/I815 DDC Adapter", | |
190 | .algo_data = &i810_ddc_bit_data, | |
191 | }; | |
192 | ||
193 | static struct pci_device_id i810_ids[] __devinitdata = { | |
194 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) }, | |
195 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) }, | |
196 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) }, | |
197 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) }, | |
198 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) }, | |
199 | { 0, }, | |
200 | }; | |
201 | ||
202 | MODULE_DEVICE_TABLE (pci, i810_ids); | |
203 | ||
204 | static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id) | |
205 | { | |
206 | int retval; | |
207 | ||
208 | retval = config_i810(dev); | |
209 | if (retval) | |
210 | return retval; | |
211 | dev_info(&dev->dev, "i810/i815 i2c device found.\n"); | |
212 | ||
213 | /* set up the sysfs linkage to our parent device */ | |
214 | i810_i2c_adapter.dev.parent = &dev->dev; | |
215 | i810_ddc_adapter.dev.parent = &dev->dev; | |
216 | ||
217 | retval = i2c_bit_add_bus(&i810_i2c_adapter); | |
218 | if (retval) | |
219 | return retval; | |
220 | retval = i2c_bit_add_bus(&i810_ddc_adapter); | |
221 | if (retval) | |
3269711b | 222 | i2c_del_adapter(&i810_i2c_adapter); |
1da177e4 LT |
223 | return retval; |
224 | } | |
225 | ||
226 | static void __devexit i810_remove(struct pci_dev *dev) | |
227 | { | |
3269711b JD |
228 | i2c_del_adapter(&i810_ddc_adapter); |
229 | i2c_del_adapter(&i810_i2c_adapter); | |
1da177e4 LT |
230 | iounmap(ioaddr); |
231 | } | |
232 | ||
233 | static struct pci_driver i810_driver = { | |
234 | .name = "i810_smbus", | |
235 | .id_table = i810_ids, | |
236 | .probe = i810_probe, | |
237 | .remove = __devexit_p(i810_remove), | |
238 | }; | |
239 | ||
240 | static int __init i2c_i810_init(void) | |
241 | { | |
242 | return pci_register_driver(&i810_driver); | |
243 | } | |
244 | ||
245 | static void __exit i2c_i810_exit(void) | |
246 | { | |
247 | pci_unregister_driver(&i810_driver); | |
248 | } | |
249 | ||
250 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " | |
251 | "Philip Edelbrock <phil@netroedge.com>, " | |
252 | "Ralph Metzler <rjkm@thp.uni-koeln.de>, " | |
253 | "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); | |
254 | MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); | |
255 | MODULE_LICENSE("GPL"); | |
256 | ||
257 | module_init(i2c_i810_init); | |
258 | module_exit(i2c_i810_exit); |