This source file includes following definitions.
- sis5595_read
- sis5595_write
- sis5595_setup
- sis5595_transaction
- sis5595_access
- sis5595_func
- sis5595_probe
- i2c_sis5595_init
- i2c_sis5595_exit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 #include <linux/kernel.h>
45 #include <linux/module.h>
46 #include <linux/delay.h>
47 #include <linux/pci.h>
48 #include <linux/ioport.h>
49 #include <linux/init.h>
50 #include <linux/i2c.h>
51 #include <linux/acpi.h>
52 #include <linux/io.h>
53
54 static int blacklist[] = {
55 PCI_DEVICE_ID_SI_540,
56 PCI_DEVICE_ID_SI_550,
57 PCI_DEVICE_ID_SI_630,
58 PCI_DEVICE_ID_SI_645,
59 PCI_DEVICE_ID_SI_646,
60 PCI_DEVICE_ID_SI_648,
61 PCI_DEVICE_ID_SI_650,
62 PCI_DEVICE_ID_SI_651,
63 PCI_DEVICE_ID_SI_730,
64 PCI_DEVICE_ID_SI_735,
65 PCI_DEVICE_ID_SI_745,
66 PCI_DEVICE_ID_SI_746,
67 PCI_DEVICE_ID_SI_5511,
68
69
70 PCI_DEVICE_ID_SI_5597,
71 PCI_DEVICE_ID_SI_5598,
72 0,
73 };
74
75
76 #define SIS5595_EXTENT 8
77
78 #define SMB_STS_LO 0x00
79 #define SMB_STS_HI 0x01
80 #define SMB_CTL_LO 0x02
81 #define SMB_CTL_HI 0x03
82 #define SMB_ADDR 0x04
83 #define SMB_CMD 0x05
84 #define SMB_PCNT 0x06
85 #define SMB_CNT 0x07
86 #define SMB_BYTE 0x08
87 #define SMB_DEV 0x10
88 #define SMB_DB0 0x11
89 #define SMB_DB1 0x12
90 #define SMB_HAA 0x13
91
92
93 #define SMB_INDEX 0x38
94 #define SMB_DAT 0x39
95 #define SIS5595_ENABLE_REG 0x40
96 #define ACPI_BASE 0x90
97
98
99 #define MAX_TIMEOUT 500
100
101
102 #define SIS5595_QUICK 0x00
103 #define SIS5595_BYTE 0x02
104 #define SIS5595_BYTE_DATA 0x04
105 #define SIS5595_WORD_DATA 0x06
106 #define SIS5595_PROC_CALL 0x08
107 #define SIS5595_BLOCK_DATA 0x0A
108
109
110
111
112
113 static u16 force_addr;
114 module_param_hw(force_addr, ushort, ioport, 0);
115 MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller");
116
117 static struct pci_driver sis5595_driver;
118 static unsigned short sis5595_base;
119 static struct pci_dev *sis5595_pdev;
120
121 static u8 sis5595_read(u8 reg)
122 {
123 outb(reg, sis5595_base + SMB_INDEX);
124 return inb(sis5595_base + SMB_DAT);
125 }
126
127 static void sis5595_write(u8 reg, u8 data)
128 {
129 outb(reg, sis5595_base + SMB_INDEX);
130 outb(data, sis5595_base + SMB_DAT);
131 }
132
133 static int sis5595_setup(struct pci_dev *SIS5595_dev)
134 {
135 u16 a;
136 u8 val;
137 int *i;
138 int retval;
139
140
141 for (i = blacklist; *i != 0; i++) {
142 struct pci_dev *dev;
143 dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
144 if (dev) {
145 dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
146 pci_dev_put(dev);
147 return -ENODEV;
148 }
149 }
150
151
152 pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
153 if (sis5595_base == 0 && force_addr == 0) {
154 dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
155 return -ENODEV;
156 }
157
158 if (force_addr)
159 sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
160 dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base);
161
162
163
164 retval = acpi_check_region(sis5595_base + SMB_INDEX, 2,
165 sis5595_driver.name);
166 if (retval)
167 return retval;
168
169 if (!request_region(sis5595_base + SMB_INDEX, 2,
170 sis5595_driver.name)) {
171 dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n",
172 sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1);
173 return -ENODEV;
174 }
175
176 if (force_addr) {
177 dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base);
178 if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)
179 != PCIBIOS_SUCCESSFUL)
180 goto error;
181 if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)
182 != PCIBIOS_SUCCESSFUL)
183 goto error;
184 if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
185
186 dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n");
187 goto error;
188 }
189 }
190
191 if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
192 != PCIBIOS_SUCCESSFUL)
193 goto error;
194 if ((val & 0x80) == 0) {
195 dev_info(&SIS5595_dev->dev, "enabling ACPI\n");
196 if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80)
197 != PCIBIOS_SUCCESSFUL)
198 goto error;
199 if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
200 != PCIBIOS_SUCCESSFUL)
201 goto error;
202 if ((val & 0x80) == 0) {
203
204 dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n");
205 goto error;
206 }
207 }
208
209
210 return 0;
211
212 error:
213 release_region(sis5595_base + SMB_INDEX, 2);
214 return -ENODEV;
215 }
216
217 static int sis5595_transaction(struct i2c_adapter *adap)
218 {
219 int temp;
220 int result = 0;
221 int timeout = 0;
222
223
224 temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
225 if (temp != 0x00) {
226 dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp);
227 sis5595_write(SMB_STS_LO, temp & 0xff);
228 sis5595_write(SMB_STS_HI, temp >> 8);
229 if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
230 dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
231 return -EBUSY;
232 } else {
233 dev_dbg(&adap->dev, "Successful!\n");
234 }
235 }
236
237
238 sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
239
240
241 do {
242 msleep(1);
243 temp = sis5595_read(SMB_STS_LO);
244 } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
245
246
247 if (timeout > MAX_TIMEOUT) {
248 dev_dbg(&adap->dev, "SMBus Timeout!\n");
249 result = -ETIMEDOUT;
250 }
251
252 if (temp & 0x10) {
253 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
254 result = -ENXIO;
255 }
256
257 if (temp & 0x20) {
258 dev_err(&adap->dev, "Bus collision! SMBus may be locked until "
259 "next hard reset (or not...)\n");
260
261 result = -EIO;
262 }
263
264 temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
265 if (temp != 0x00) {
266 sis5595_write(SMB_STS_LO, temp & 0xff);
267 sis5595_write(SMB_STS_HI, temp >> 8);
268 }
269
270 temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
271 if (temp != 0x00)
272 dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp);
273
274 return result;
275 }
276
277
278 static s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
279 unsigned short flags, char read_write,
280 u8 command, int size, union i2c_smbus_data *data)
281 {
282 int status;
283
284 switch (size) {
285 case I2C_SMBUS_QUICK:
286 sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
287 size = SIS5595_QUICK;
288 break;
289 case I2C_SMBUS_BYTE:
290 sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
291 if (read_write == I2C_SMBUS_WRITE)
292 sis5595_write(SMB_CMD, command);
293 size = SIS5595_BYTE;
294 break;
295 case I2C_SMBUS_BYTE_DATA:
296 sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
297 sis5595_write(SMB_CMD, command);
298 if (read_write == I2C_SMBUS_WRITE)
299 sis5595_write(SMB_BYTE, data->byte);
300 size = SIS5595_BYTE_DATA;
301 break;
302 case I2C_SMBUS_PROC_CALL:
303 case I2C_SMBUS_WORD_DATA:
304 sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
305 sis5595_write(SMB_CMD, command);
306 if (read_write == I2C_SMBUS_WRITE) {
307 sis5595_write(SMB_BYTE, data->word & 0xff);
308 sis5595_write(SMB_BYTE + 1,
309 (data->word & 0xff00) >> 8);
310 }
311 size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA;
312 break;
313 default:
314 dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
315 return -EOPNOTSUPP;
316 }
317
318 sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
319
320 status = sis5595_transaction(adap);
321 if (status)
322 return status;
323
324 if ((size != SIS5595_PROC_CALL) &&
325 ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
326 return 0;
327
328
329 switch (size) {
330 case SIS5595_BYTE:
331 case SIS5595_BYTE_DATA:
332 data->byte = sis5595_read(SMB_BYTE);
333 break;
334 case SIS5595_WORD_DATA:
335 case SIS5595_PROC_CALL:
336 data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8);
337 break;
338 }
339 return 0;
340 }
341
342 static u32 sis5595_func(struct i2c_adapter *adapter)
343 {
344 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
345 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
346 I2C_FUNC_SMBUS_PROC_CALL;
347 }
348
349 static const struct i2c_algorithm smbus_algorithm = {
350 .smbus_xfer = sis5595_access,
351 .functionality = sis5595_func,
352 };
353
354 static struct i2c_adapter sis5595_adapter = {
355 .owner = THIS_MODULE,
356 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
357 .algo = &smbus_algorithm,
358 };
359
360 static const struct pci_device_id sis5595_ids[] = {
361 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
362 { 0, }
363 };
364
365 MODULE_DEVICE_TABLE (pci, sis5595_ids);
366
367 static int sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
368 {
369 int err;
370
371 if (sis5595_setup(dev)) {
372 dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
373 return -ENODEV;
374 }
375
376
377 sis5595_adapter.dev.parent = &dev->dev;
378
379 snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name),
380 "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX);
381 err = i2c_add_adapter(&sis5595_adapter);
382 if (err) {
383 release_region(sis5595_base + SMB_INDEX, 2);
384 return err;
385 }
386
387
388
389
390
391 sis5595_pdev = pci_dev_get(dev);
392 return -ENODEV;
393 }
394
395 static struct pci_driver sis5595_driver = {
396 .name = "sis5595_smbus",
397 .id_table = sis5595_ids,
398 .probe = sis5595_probe,
399 };
400
401 static int __init i2c_sis5595_init(void)
402 {
403 return pci_register_driver(&sis5595_driver);
404 }
405
406 static void __exit i2c_sis5595_exit(void)
407 {
408 pci_unregister_driver(&sis5595_driver);
409 if (sis5595_pdev) {
410 i2c_del_adapter(&sis5595_adapter);
411 release_region(sis5595_base + SMB_INDEX, 2);
412 pci_dev_put(sis5595_pdev);
413 sis5595_pdev = NULL;
414 }
415 }
416
417 MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
418 MODULE_DESCRIPTION("SIS5595 SMBus driver");
419 MODULE_LICENSE("GPL");
420
421 module_init(i2c_sis5595_init);
422 module_exit(i2c_sis5595_exit);