Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com> |
3 | ||
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation; either version 2 of the License, or | |
7 | (at your option) any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software | |
16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | */ | |
18 | ||
19 | /* | |
20 | This module must be considered BETA unless and until | |
21 | the chipset manufacturer releases a datasheet. | |
22 | The register definitions are based on the SiS630. | |
23 | ||
24 | This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c) | |
25 | for just about every machine for which users have reported. | |
26 | If this module isn't detecting your 96x south bridge, have a | |
27 | look there. | |
28 | ||
29 | We assume there can only be one SiS96x with one SMBus interface. | |
30 | */ | |
31 | ||
1da177e4 LT |
32 | #include <linux/module.h> |
33 | #include <linux/pci.h> | |
34 | #include <linux/kernel.h> | |
35 | #include <linux/delay.h> | |
36 | #include <linux/stddef.h> | |
1da177e4 LT |
37 | #include <linux/ioport.h> |
38 | #include <linux/i2c.h> | |
39 | #include <linux/init.h> | |
54fb4a05 | 40 | #include <linux/acpi.h> |
1da177e4 LT |
41 | #include <asm/io.h> |
42 | ||
1da177e4 LT |
43 | /* base address register in PCI config space */ |
44 | #define SIS96x_BAR 0x04 | |
45 | ||
46 | /* SiS96x SMBus registers */ | |
47 | #define SMB_STS 0x00 | |
48 | #define SMB_EN 0x01 | |
49 | #define SMB_CNT 0x02 | |
50 | #define SMB_HOST_CNT 0x03 | |
51 | #define SMB_ADDR 0x04 | |
52 | #define SMB_CMD 0x05 | |
53 | #define SMB_PCOUNT 0x06 | |
54 | #define SMB_COUNT 0x07 | |
55 | #define SMB_BYTE 0x08 | |
56 | #define SMB_DEV_ADDR 0x10 | |
57 | #define SMB_DB0 0x11 | |
58 | #define SMB_DB1 0x12 | |
59 | #define SMB_SAA 0x13 | |
60 | ||
61 | /* register count for request_region */ | |
62 | #define SMB_IOSIZE 0x20 | |
63 | ||
64 | /* Other settings */ | |
65 | #define MAX_TIMEOUT 500 | |
66 | ||
67 | /* SiS96x SMBus constants */ | |
68 | #define SIS96x_QUICK 0x00 | |
69 | #define SIS96x_BYTE 0x01 | |
70 | #define SIS96x_BYTE_DATA 0x02 | |
71 | #define SIS96x_WORD_DATA 0x03 | |
72 | #define SIS96x_PROC_CALL 0x04 | |
73 | #define SIS96x_BLOCK_DATA 0x05 | |
74 | ||
d6072f84 | 75 | static struct pci_driver sis96x_driver; |
1da177e4 | 76 | static struct i2c_adapter sis96x_adapter; |
60507095 | 77 | static u16 sis96x_smbus_base; |
1da177e4 LT |
78 | |
79 | static inline u8 sis96x_read(u8 reg) | |
80 | { | |
81 | return inb(sis96x_smbus_base + reg) ; | |
82 | } | |
83 | ||
84 | static inline void sis96x_write(u8 reg, u8 data) | |
85 | { | |
86 | outb(data, sis96x_smbus_base + reg) ; | |
87 | } | |
88 | ||
89 | /* Execute a SMBus transaction. | |
90 | int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA | |
91 | */ | |
92 | static int sis96x_transaction(int size) | |
93 | { | |
94 | int temp; | |
95 | int result = 0; | |
96 | int timeout = 0; | |
97 | ||
98 | dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size); | |
99 | ||
100 | /* Make sure the SMBus host is ready to start transmitting */ | |
101 | if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { | |
102 | ||
103 | dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). " | |
104 | "Resetting...\n", temp); | |
105 | ||
106 | /* kill the transaction */ | |
107 | sis96x_write(SMB_HOST_CNT, 0x20); | |
108 | ||
109 | /* check it again */ | |
110 | if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { | |
111 | dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp); | |
97140342 | 112 | return -EBUSY; |
1da177e4 LT |
113 | } else { |
114 | dev_dbg(&sis96x_adapter.dev, "Successful\n"); | |
115 | } | |
116 | } | |
117 | ||
118 | /* Turn off timeout interrupts, set fast host clock */ | |
119 | sis96x_write(SMB_CNT, 0x20); | |
120 | ||
121 | /* clear all (sticky) status flags */ | |
122 | temp = sis96x_read(SMB_STS); | |
123 | sis96x_write(SMB_STS, temp & 0x1e); | |
124 | ||
125 | /* start the transaction by setting bit 4 and size bits */ | |
126 | sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); | |
127 | ||
128 | /* We will always wait for a fraction of a second! */ | |
129 | do { | |
130 | msleep(1); | |
131 | temp = sis96x_read(SMB_STS); | |
132 | } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); | |
133 | ||
134 | /* If the SMBus is still busy, we give up */ | |
135 | if (timeout >= MAX_TIMEOUT) { | |
136 | dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp); | |
97140342 | 137 | result = -ETIMEDOUT; |
1da177e4 LT |
138 | } |
139 | ||
140 | /* device error - probably missing ACK */ | |
141 | if (temp & 0x02) { | |
142 | dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n"); | |
97140342 | 143 | result = -ENXIO; |
1da177e4 LT |
144 | } |
145 | ||
146 | /* bus collision */ | |
147 | if (temp & 0x04) { | |
148 | dev_dbg(&sis96x_adapter.dev, "Bus collision!\n"); | |
97140342 | 149 | result = -EIO; |
1da177e4 LT |
150 | } |
151 | ||
152 | /* Finish up by resetting the bus */ | |
153 | sis96x_write(SMB_STS, temp); | |
154 | if ((temp = sis96x_read(SMB_STS))) { | |
155 | dev_dbg(&sis96x_adapter.dev, "Failed reset at " | |
156 | "end of transaction! (0x%02x)\n", temp); | |
157 | } | |
158 | ||
159 | return result; | |
160 | } | |
161 | ||
97140342 | 162 | /* Return negative errno on error. */ |
1da177e4 LT |
163 | static s32 sis96x_access(struct i2c_adapter * adap, u16 addr, |
164 | unsigned short flags, char read_write, | |
165 | u8 command, int size, union i2c_smbus_data * data) | |
166 | { | |
97140342 | 167 | int status; |
1da177e4 LT |
168 | |
169 | switch (size) { | |
170 | case I2C_SMBUS_QUICK: | |
171 | sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | |
172 | size = SIS96x_QUICK; | |
173 | break; | |
174 | ||
175 | case I2C_SMBUS_BYTE: | |
176 | sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | |
177 | if (read_write == I2C_SMBUS_WRITE) | |
178 | sis96x_write(SMB_CMD, command); | |
179 | size = SIS96x_BYTE; | |
180 | break; | |
181 | ||
182 | case I2C_SMBUS_BYTE_DATA: | |
183 | sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | |
184 | sis96x_write(SMB_CMD, command); | |
185 | if (read_write == I2C_SMBUS_WRITE) | |
186 | sis96x_write(SMB_BYTE, data->byte); | |
187 | size = SIS96x_BYTE_DATA; | |
188 | break; | |
189 | ||
190 | case I2C_SMBUS_PROC_CALL: | |
191 | case I2C_SMBUS_WORD_DATA: | |
192 | sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | |
193 | sis96x_write(SMB_CMD, command); | |
194 | if (read_write == I2C_SMBUS_WRITE) { | |
195 | sis96x_write(SMB_BYTE, data->word & 0xff); | |
196 | sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); | |
197 | } | |
198 | size = (size == I2C_SMBUS_PROC_CALL ? | |
199 | SIS96x_PROC_CALL : SIS96x_WORD_DATA); | |
200 | break; | |
201 | ||
1da177e4 | 202 | default: |
ac7fc4fb | 203 | dev_warn(&adap->dev, "Unsupported transaction %d\n", size); |
97140342 | 204 | return -EOPNOTSUPP; |
1da177e4 LT |
205 | } |
206 | ||
97140342 DB |
207 | status = sis96x_transaction(size); |
208 | if (status) | |
209 | return status; | |
1da177e4 LT |
210 | |
211 | if ((size != SIS96x_PROC_CALL) && | |
212 | ((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK))) | |
213 | return 0; | |
214 | ||
215 | switch (size) { | |
216 | case SIS96x_BYTE: | |
217 | case SIS96x_BYTE_DATA: | |
218 | data->byte = sis96x_read(SMB_BYTE); | |
219 | break; | |
220 | ||
221 | case SIS96x_WORD_DATA: | |
222 | case SIS96x_PROC_CALL: | |
223 | data->word = sis96x_read(SMB_BYTE) + | |
224 | (sis96x_read(SMB_BYTE + 1) << 8); | |
225 | break; | |
226 | } | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static u32 sis96x_func(struct i2c_adapter *adapter) | |
231 | { | |
232 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | |
233 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | | |
234 | I2C_FUNC_SMBUS_PROC_CALL; | |
235 | } | |
236 | ||
8f9082c5 | 237 | static const struct i2c_algorithm smbus_algorithm = { |
1da177e4 LT |
238 | .smbus_xfer = sis96x_access, |
239 | .functionality = sis96x_func, | |
240 | }; | |
241 | ||
242 | static struct i2c_adapter sis96x_adapter = { | |
243 | .owner = THIS_MODULE, | |
3401b2ff | 244 | .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, |
1da177e4 | 245 | .algo = &smbus_algorithm, |
1da177e4 LT |
246 | }; |
247 | ||
248 | static struct pci_device_id sis96x_ids[] = { | |
249 | { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) }, | |
250 | { 0, } | |
251 | }; | |
252 | ||
253 | MODULE_DEVICE_TABLE (pci, sis96x_ids); | |
254 | ||
255 | static int __devinit sis96x_probe(struct pci_dev *dev, | |
256 | const struct pci_device_id *id) | |
257 | { | |
258 | u16 ww = 0; | |
259 | int retval; | |
260 | ||
261 | if (sis96x_smbus_base) { | |
262 | dev_err(&dev->dev, "Only one device supported.\n"); | |
263 | return -EBUSY; | |
264 | } | |
265 | ||
266 | pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); | |
267 | if (PCI_CLASS_SERIAL_SMBUS != ww) { | |
268 | dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); | |
269 | return -ENODEV; | |
270 | } | |
271 | ||
272 | sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); | |
273 | if (!sis96x_smbus_base) { | |
274 | dev_err(&dev->dev, "SiS96x SMBus base address " | |
275 | "not initialized!\n"); | |
276 | return -EINVAL; | |
277 | } | |
278 | dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", | |
279 | sis96x_smbus_base); | |
280 | ||
54fb4a05 JD |
281 | retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]); |
282 | if (retval) | |
283 | return retval; | |
284 | ||
1da177e4 | 285 | /* Everything is happy, let's grab the memory and set things up. */ |
d6072f84 JD |
286 | if (!request_region(sis96x_smbus_base, SMB_IOSIZE, |
287 | sis96x_driver.name)) { | |
1da177e4 LT |
288 | dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " |
289 | "already in use!\n", sis96x_smbus_base, | |
290 | sis96x_smbus_base + SMB_IOSIZE - 1); | |
291 | ||
292 | sis96x_smbus_base = 0; | |
293 | return -EINVAL; | |
294 | } | |
295 | ||
405ae7d3 | 296 | /* set up the sysfs linkage to our parent device */ |
1da177e4 LT |
297 | sis96x_adapter.dev.parent = &dev->dev; |
298 | ||
2096b956 | 299 | snprintf(sis96x_adapter.name, sizeof(sis96x_adapter.name), |
1da177e4 LT |
300 | "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); |
301 | ||
302 | if ((retval = i2c_add_adapter(&sis96x_adapter))) { | |
303 | dev_err(&dev->dev, "Couldn't register adapter!\n"); | |
304 | release_region(sis96x_smbus_base, SMB_IOSIZE); | |
305 | sis96x_smbus_base = 0; | |
306 | } | |
307 | ||
308 | return retval; | |
309 | } | |
310 | ||
311 | static void __devexit sis96x_remove(struct pci_dev *dev) | |
312 | { | |
313 | if (sis96x_smbus_base) { | |
314 | i2c_del_adapter(&sis96x_adapter); | |
315 | release_region(sis96x_smbus_base, SMB_IOSIZE); | |
316 | sis96x_smbus_base = 0; | |
317 | } | |
318 | } | |
319 | ||
320 | static struct pci_driver sis96x_driver = { | |
321 | .name = "sis96x_smbus", | |
322 | .id_table = sis96x_ids, | |
323 | .probe = sis96x_probe, | |
324 | .remove = __devexit_p(sis96x_remove), | |
325 | }; | |
326 | ||
327 | static int __init i2c_sis96x_init(void) | |
328 | { | |
1da177e4 LT |
329 | return pci_register_driver(&sis96x_driver); |
330 | } | |
331 | ||
332 | static void __exit i2c_sis96x_exit(void) | |
333 | { | |
334 | pci_unregister_driver(&sis96x_driver); | |
335 | } | |
336 | ||
337 | MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); | |
338 | MODULE_DESCRIPTION("SiS96x SMBus driver"); | |
339 | MODULE_LICENSE("GPL"); | |
340 | ||
341 | /* Register initialization functions using helper macros */ | |
342 | module_init(i2c_sis96x_init); | |
343 | module_exit(i2c_sis96x_exit); | |
344 |