blob: f58455e7689e57da7aff99a086a4f6846153b329 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
3 monitoring
4
5 Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22/*
23 Changes:
24 24.08.2002
25 Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
26 Changed sis630_transaction.(Thanks to Mark M. Hoffman)
27 18.09.2002
28 Added SIS730 as supported.
29 21.09.2002
30 Added high_clock module option.If this option is set
31 used Host Master Clock 56KHz (default 14KHz).For now we save old Host
32 Master Clock and after transaction completed restore (otherwise
33 it's confuse BIOS and hung Machine).
34 24.09.2002
35 Fixed typo in sis630_access
36 Fixed logical error by restoring of Host Master Clock
37 31.07.2003
38 Added block data read/write support.
39*/
40
41/*
42 Status: beta
43
44 Supports:
45 SIS 630
46 SIS 730
47
48 Note: we assume there can only be one device, with one SMBus interface.
49*/
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <linux/kernel.h>
52#include <linux/module.h>
53#include <linux/delay.h>
54#include <linux/pci.h>
55#include <linux/ioport.h>
56#include <linux/init.h>
57#include <linux/i2c.h>
58#include <asm/io.h>
59
60/* SIS630 SMBus registers */
61#define SMB_STS 0x80 /* status */
62#define SMB_EN 0x81 /* status enable */
63#define SMB_CNT 0x82
64#define SMBHOST_CNT 0x83
65#define SMB_ADDR 0x84
66#define SMB_CMD 0x85
67#define SMB_PCOUNT 0x86 /* processed count */
68#define SMB_COUNT 0x87
69#define SMB_BYTE 0x88 /* ~0x8F data byte field */
70#define SMBDEV_ADDR 0x90
71#define SMB_DB0 0x91
72#define SMB_DB1 0x92
73#define SMB_SAA 0x93
74
75/* register count for request_region */
76#define SIS630_SMB_IOREGION 20
77
78/* PCI address constants */
79/* acpi base address register */
80#define SIS630_ACPI_BASE_REG 0x74
81/* bios control register */
82#define SIS630_BIOS_CTL_REG 0x40
83
84/* Other settings */
85#define MAX_TIMEOUT 500
86
87/* SIS630 constants */
88#define SIS630_QUICK 0x00
89#define SIS630_BYTE 0x01
90#define SIS630_BYTE_DATA 0x02
91#define SIS630_WORD_DATA 0x03
92#define SIS630_PCALL 0x04
93#define SIS630_BLOCK_DATA 0x05
94
95/* insmod parameters */
96static int high_clock;
97static int force;
98module_param(high_clock, bool, 0);
99MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
100module_param(force, bool, 0);
101MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
102
103/* acpi base address */
104static unsigned short acpi_base = 0;
105
106/* supported chips */
107static int supported[] = {
108 PCI_DEVICE_ID_SI_630,
109 PCI_DEVICE_ID_SI_730,
110 0 /* terminates the list */
111};
112
113static inline u8 sis630_read(u8 reg)
114{
115 return inb(acpi_base + reg);
116}
117
118static inline void sis630_write(u8 reg, u8 data)
119{
120 outb(data, acpi_base + reg);
121}
122
123static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
124{
125 int temp;
126
127 /* Make sure the SMBus host is ready to start transmitting. */
128 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
129 dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
130 /* kill smbus transaction */
131 sis630_write(SMBHOST_CNT, 0x20);
132
133 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
134 dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
135 return -1;
136 } else {
137 dev_dbg(&adap->dev, "Successfull!\n");
138 }
139 }
140
141 /* save old clock, so we can prevent machine for hung */
142 *oldclock = sis630_read(SMB_CNT);
143
144 dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
145
146 /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
147 if (high_clock)
148 sis630_write(SMB_CNT, 0x20);
149 else
150 sis630_write(SMB_CNT, (*oldclock & ~0x40));
151
152 /* clear all sticky bits */
153 temp = sis630_read(SMB_STS);
154 sis630_write(SMB_STS, temp & 0x1e);
155
156 /* start the transaction by setting bit 4 and size */
157 sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
158
159 return 0;
160}
161
162static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
163{
164 int temp, result = 0, timeout = 0;
165
166 /* We will always wait for a fraction of a second! */
167 do {
168 msleep(1);
169 temp = sis630_read(SMB_STS);
170 /* check if block transmitted */
171 if (size == SIS630_BLOCK_DATA && (temp & 0x10))
172 break;
173 } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
174
175 /* If the SMBus is still busy, we give up */
176 if (timeout >= MAX_TIMEOUT) {
177 dev_dbg(&adap->dev, "SMBus Timeout!\n");
178 result = -1;
179 }
180
181 if (temp & 0x02) {
182 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
183 result = -1;
184 }
185
186 if (temp & 0x04) {
187 dev_err(&adap->dev, "Bus collision!\n");
188 result = -1;
189 /*
190 TBD: Datasheet say:
191 the software should clear this bit and restart SMBUS operation.
192 Should we do it or user start request again?
193 */
194 }
195
196 return result;
197}
198
199static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
200{
201 int temp = 0;
202
203 /* clear all status "sticky" bits */
204 sis630_write(SMB_STS, temp);
205
206 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
207
208 /*
209 * restore old Host Master Clock if high_clock is set
210 * and oldclock was not 56KHz
211 */
212 if (high_clock && !(oldclock & 0x20))
213 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
214
215 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
216}
217
218static int sis630_transaction(struct i2c_adapter *adap, int size)
219{
220 int result = 0;
221 u8 oldclock = 0;
222
223 result = sis630_transaction_start(adap, size, &oldclock);
224 if (!result) {
225 result = sis630_transaction_wait(adap, size);
226 sis630_transaction_end(adap, oldclock);
227 }
228
229 return result;
230}
231
232static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
233{
234 int i, len = 0, rc = 0;
235 u8 oldclock = 0;
236
237 if (read_write == I2C_SMBUS_WRITE) {
238 len = data->block[0];
239 if (len < 0)
240 len = 0;
241 else if (len > 32)
242 len = 32;
243 sis630_write(SMB_COUNT, len);
244 for (i=1; i <= len; i++) {
245 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
246 /* set data */
247 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
248 if (i==8 || (len<8 && i==len)) {
249 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
250 /* first transaction */
251 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock))
252 return -1;
253 }
254 else if ((i-1)%8 == 7 || i==len) {
255 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
256 if (i>8) {
257 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
258 /*
259 If this is not first transaction,
260 we must clear sticky bit.
261 clear SMBARY_STS
262 */
263 sis630_write(SMB_STS,0x10);
264 }
265 if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
266 dev_dbg(&adap->dev, "trans_wait failed\n");
267 rc = -1;
268 break;
269 }
270 }
271 }
272 }
273 else {
274 /* read request */
275 data->block[0] = len = 0;
276 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock)) {
277 return -1;
278 }
279 do {
280 if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
281 dev_dbg(&adap->dev, "trans_wait failed\n");
282 rc = -1;
283 break;
284 }
285 /* if this first transaction then read byte count */
286 if (len == 0)
287 data->block[0] = sis630_read(SMB_COUNT);
288
289 /* just to be sure */
290 if (data->block[0] > 32)
291 data->block[0] = 32;
292
293 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
294
295 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
296 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
297 data->block[len+1] = sis630_read(SMB_BYTE+i);
298 }
299
300 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
301
302 /* clear SMBARY_STS */
303 sis630_write(SMB_STS,0x10);
304 } while(len < data->block[0]);
305 }
306
307 sis630_transaction_end(adap, oldclock);
308
309 return rc;
310}
311
312/* Return -1 on error. */
313static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
314 unsigned short flags, char read_write,
315 u8 command, int size, union i2c_smbus_data *data)
316{
317 switch (size) {
318 case I2C_SMBUS_QUICK:
319 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
320 size = SIS630_QUICK;
321 break;
322 case I2C_SMBUS_BYTE:
323 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
324 if (read_write == I2C_SMBUS_WRITE)
325 sis630_write(SMB_CMD, command);
326 size = SIS630_BYTE;
327 break;
328 case I2C_SMBUS_BYTE_DATA:
329 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
330 sis630_write(SMB_CMD, command);
331 if (read_write == I2C_SMBUS_WRITE)
332 sis630_write(SMB_BYTE, data->byte);
333 size = SIS630_BYTE_DATA;
334 break;
335 case I2C_SMBUS_PROC_CALL:
336 case I2C_SMBUS_WORD_DATA:
337 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
338 sis630_write(SMB_CMD, command);
339 if (read_write == I2C_SMBUS_WRITE) {
340 sis630_write(SMB_BYTE, data->word & 0xff);
341 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
342 }
343 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
344 break;
345 case I2C_SMBUS_BLOCK_DATA:
346 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
347 sis630_write(SMB_CMD, command);
348 size = SIS630_BLOCK_DATA;
349 return sis630_block_data(adap, data, read_write);
350 default:
351 printk("Unsupported I2C size\n");
352 return -1;
353 break;
354 }
355
356 if (sis630_transaction(adap, size))
357 return -1;
358
359 if ((size != SIS630_PCALL) &&
360 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
361 return 0;
362 }
363
364 switch(size) {
365 case SIS630_BYTE:
366 case SIS630_BYTE_DATA:
367 data->byte = sis630_read(SMB_BYTE);
368 break;
369 case SIS630_PCALL:
370 case SIS630_WORD_DATA:
371 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
372 break;
373 default:
374 return -1;
375 break;
376 }
377
378 return 0;
379}
380
381static u32 sis630_func(struct i2c_adapter *adapter)
382{
383 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
384 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
385 I2C_FUNC_SMBUS_BLOCK_DATA;
386}
387
388static int sis630_setup(struct pci_dev *sis630_dev)
389{
390 unsigned char b;
391 struct pci_dev *dummy = NULL;
392 int retval = -ENODEV, i;
393
394 /* check for supported SiS devices */
395 for (i=0; supported[i] > 0 ; i++) {
396 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
397 break; /* found */
398 }
399
400 if (dummy) {
401 pci_dev_put(dummy);
402 }
403 else if (force) {
404 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
405 "loading because of force option enabled\n");
406 }
407 else {
408 return -ENODEV;
409 }
410
411 /*
412 Enable ACPI first , so we can accsess reg 74-75
413 in acpi io space and read acpi base addr
414 */
415 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
416 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
417 goto exit;
418 }
419 /* if ACPI already enabled , do nothing */
420 if (!(b & 0x80) &&
421 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
422 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
423 goto exit;
424 }
425
426 /* Determine the ACPI base address */
427 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
428 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
429 goto exit;
430 }
431
432 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
433
434 /* Everything is happy, let's grab the memory and set things up. */
435 if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")) {
436 dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
437 "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
438 goto exit;
439 }
440
441 retval = 0;
442
443exit:
444 if (retval)
445 acpi_base = 0;
446 return retval;
447}
448
449
450static struct i2c_algorithm smbus_algorithm = {
451 .name = "Non-I2C SMBus adapter",
452 .id = I2C_ALGO_SMBUS,
453 .smbus_xfer = sis630_access,
454 .functionality = sis630_func,
455};
456
457static struct i2c_adapter sis630_adapter = {
458 .owner = THIS_MODULE,
459 .class = I2C_CLASS_HWMON,
460 .name = "unset",
461 .algo = &smbus_algorithm,
462};
463
464static struct pci_device_id sis630_ids[] __devinitdata = {
465 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
466 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
467 { 0, }
468};
469
470MODULE_DEVICE_TABLE (pci, sis630_ids);
471
472static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
473{
474 if (sis630_setup(dev)) {
475 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
476 return -ENODEV;
477 }
478
479 /* set up the driverfs linkage to our parent device */
480 sis630_adapter.dev.parent = &dev->dev;
481
482 sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
483 acpi_base + SMB_STS);
484
485 return i2c_add_adapter(&sis630_adapter);
486}
487
488static void __devexit sis630_remove(struct pci_dev *dev)
489{
490 if (acpi_base) {
491 i2c_del_adapter(&sis630_adapter);
492 release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
493 acpi_base = 0;
494 }
495}
496
497
498static struct pci_driver sis630_driver = {
499 .name = "sis630_smbus",
500 .id_table = sis630_ids,
501 .probe = sis630_probe,
502 .remove = __devexit_p(sis630_remove),
503};
504
505static int __init i2c_sis630_init(void)
506{
507 return pci_register_driver(&sis630_driver);
508}
509
510
511static void __exit i2c_sis630_exit(void)
512{
513 pci_unregister_driver(&sis630_driver);
514}
515
516
517MODULE_LICENSE("GPL");
518MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
519MODULE_DESCRIPTION("SIS630 SMBus driver");
520
521module_init(i2c_sis630_init);
522module_exit(i2c_sis630_exit);