Merge branch 'fix/pcm-jiffies-check' into fix/asoc
[linux-2.6] / drivers / i2c / busses / i2c-sis630.c
1 /*
2     Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
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    Changes:
21    24.08.2002
22         Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
23         Changed sis630_transaction.(Thanks to Mark M. Hoffman)
24    18.09.2002
25         Added SIS730 as supported.
26    21.09.2002
27         Added high_clock module option.If this option is set
28         used Host Master Clock 56KHz (default 14KHz).For now we save old Host
29         Master Clock and after transaction completed restore (otherwise
30         it's confuse BIOS and hung Machine).
31    24.09.2002
32         Fixed typo in sis630_access
33         Fixed logical error by restoring of Host Master Clock
34    31.07.2003
35         Added block data read/write support.
36 */
37
38 /*
39    Status: beta
40
41    Supports:
42         SIS 630
43         SIS 730
44
45    Note: we assume there can only be one device, with one SMBus interface.
46 */
47
48 #include <linux/kernel.h>
49 #include <linux/module.h>
50 #include <linux/delay.h>
51 #include <linux/pci.h>
52 #include <linux/ioport.h>
53 #include <linux/init.h>
54 #include <linux/i2c.h>
55 #include <linux/acpi.h>
56 #include <asm/io.h>
57
58 /* SIS630 SMBus registers */
59 #define SMB_STS                 0x80    /* status */
60 #define SMB_EN                  0x81    /* status enable */
61 #define SMB_CNT                 0x82
62 #define SMBHOST_CNT             0x83
63 #define SMB_ADDR                0x84
64 #define SMB_CMD                 0x85
65 #define SMB_PCOUNT              0x86    /* processed count */
66 #define SMB_COUNT               0x87
67 #define SMB_BYTE                0x88    /* ~0x8F data byte field */
68 #define SMBDEV_ADDR             0x90
69 #define SMB_DB0                 0x91
70 #define SMB_DB1                 0x92
71 #define SMB_SAA                 0x93
72
73 /* register count for request_region */
74 #define SIS630_SMB_IOREGION     20
75
76 /* PCI address constants */
77 /* acpi base address register  */
78 #define SIS630_ACPI_BASE_REG    0x74
79 /* bios control register */
80 #define SIS630_BIOS_CTL_REG     0x40
81
82 /* Other settings */
83 #define MAX_TIMEOUT             500
84
85 /* SIS630 constants */
86 #define SIS630_QUICK            0x00
87 #define SIS630_BYTE             0x01
88 #define SIS630_BYTE_DATA        0x02
89 #define SIS630_WORD_DATA        0x03
90 #define SIS630_PCALL            0x04
91 #define SIS630_BLOCK_DATA       0x05
92
93 static struct pci_driver sis630_driver;
94
95 /* insmod parameters */
96 static int high_clock;
97 static int force;
98 module_param(high_clock, bool, 0);
99 MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
100 module_param(force, bool, 0);
101 MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
102
103 /* acpi base address */
104 static unsigned short acpi_base;
105
106 /* supported chips */
107 static int supported[] = {
108         PCI_DEVICE_ID_SI_630,
109         PCI_DEVICE_ID_SI_730,
110         0 /* terminates the list */
111 };
112
113 static inline u8 sis630_read(u8 reg)
114 {
115         return inb(acpi_base + reg);
116 }
117
118 static inline void sis630_write(u8 reg, u8 data)
119 {
120         outb(data, acpi_base + reg);
121 }
122
123 static 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 -EBUSY;
136                 } else {
137                         dev_dbg(&adap->dev, "Successful!\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
162 static 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 = -ETIMEDOUT;
179         }
180
181         if (temp & 0x02) {
182                 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
183                 result = -ENXIO;
184         }
185
186         if (temp & 0x04) {
187                 dev_err(&adap->dev, "Bus collision!\n");
188                 result = -EIO;
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
199 static 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
218 static 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
232 static 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                                 rc = sis630_transaction_start(adap,
252                                                 SIS630_BLOCK_DATA, &oldclock);
253                                 if (rc)
254                                         return rc;
255                         }
256                         else if ((i-1)%8 == 7 || i==len) {
257                                 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
258                                 if (i>8) {
259                                         dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
260                                         /*
261                                            If this is not first transaction,
262                                            we must clear sticky bit.
263                                            clear SMBARY_STS
264                                         */
265                                         sis630_write(SMB_STS,0x10);
266                                 }
267                                 rc = sis630_transaction_wait(adap,
268                                                 SIS630_BLOCK_DATA);
269                                 if (rc) {
270                                         dev_dbg(&adap->dev, "trans_wait failed\n");
271                                         break;
272                                 }
273                         }
274                 }
275         }
276         else {
277                 /* read request */
278                 data->block[0] = len = 0;
279                 rc = sis630_transaction_start(adap,
280                                 SIS630_BLOCK_DATA, &oldclock);
281                 if (rc)
282                         return rc;
283                 do {
284                         rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
285                         if (rc) {
286                                 dev_dbg(&adap->dev, "trans_wait failed\n");
287                                 break;
288                         }
289                         /* if this first transaction then read byte count */
290                         if (len == 0)
291                                 data->block[0] = sis630_read(SMB_COUNT);
292
293                         /* just to be sure */
294                         if (data->block[0] > 32)
295                                 data->block[0] = 32;
296
297                         dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
298
299                         for (i=0; i < 8 && len < data->block[0]; i++,len++) {
300                                 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
301                                 data->block[len+1] = sis630_read(SMB_BYTE+i);
302                         }
303
304                         dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
305
306                         /* clear SMBARY_STS */
307                         sis630_write(SMB_STS,0x10);
308                 } while(len < data->block[0]);
309         }
310
311         sis630_transaction_end(adap, oldclock);
312
313         return rc;
314 }
315
316 /* Return negative errno on error. */
317 static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
318                          unsigned short flags, char read_write,
319                          u8 command, int size, union i2c_smbus_data *data)
320 {
321         int status;
322
323         switch (size) {
324                 case I2C_SMBUS_QUICK:
325                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
326                         size = SIS630_QUICK;
327                         break;
328                 case I2C_SMBUS_BYTE:
329                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
330                         if (read_write == I2C_SMBUS_WRITE)
331                                 sis630_write(SMB_CMD, command);
332                         size = SIS630_BYTE;
333                         break;
334                 case I2C_SMBUS_BYTE_DATA:
335                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
336                         sis630_write(SMB_CMD, command);
337                         if (read_write == I2C_SMBUS_WRITE)
338                                 sis630_write(SMB_BYTE, data->byte);
339                         size = SIS630_BYTE_DATA;
340                         break;
341                 case I2C_SMBUS_PROC_CALL:
342                 case I2C_SMBUS_WORD_DATA:
343                         sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
344                         sis630_write(SMB_CMD, command);
345                         if (read_write == I2C_SMBUS_WRITE) {
346                                 sis630_write(SMB_BYTE, data->word & 0xff);
347                                 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
348                         }
349                         size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
350                         break;
351                 case I2C_SMBUS_BLOCK_DATA:
352                         sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
353                         sis630_write(SMB_CMD, command);
354                         size = SIS630_BLOCK_DATA;
355                         return sis630_block_data(adap, data, read_write);
356                 default:
357                         dev_warn(&adap->dev, "Unsupported transaction %d\n",
358                                  size);
359                         return -EOPNOTSUPP;
360         }
361
362         status = sis630_transaction(adap, size);
363         if (status)
364                 return status;
365
366         if ((size != SIS630_PCALL) &&
367                 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
368                 return 0;
369         }
370
371         switch(size) {
372                 case SIS630_BYTE:
373                 case SIS630_BYTE_DATA:
374                         data->byte = sis630_read(SMB_BYTE);
375                         break;
376                 case SIS630_PCALL:
377                 case SIS630_WORD_DATA:
378                         data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
379                         break;
380         }
381
382         return 0;
383 }
384
385 static u32 sis630_func(struct i2c_adapter *adapter)
386 {
387         return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
388                 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
389                 I2C_FUNC_SMBUS_BLOCK_DATA;
390 }
391
392 static int sis630_setup(struct pci_dev *sis630_dev)
393 {
394         unsigned char b;
395         struct pci_dev *dummy = NULL;
396         int retval = -ENODEV, i;
397
398         /* check for supported SiS devices */
399         for (i=0; supported[i] > 0 ; i++) {
400                 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
401                         break; /* found */
402         }
403
404         if (dummy) {
405                 pci_dev_put(dummy);
406         }
407         else if (force) {
408                 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
409                         "loading because of force option enabled\n");
410         }
411         else {
412                 return -ENODEV;
413         }
414
415         /*
416            Enable ACPI first , so we can accsess reg 74-75
417            in acpi io space and read acpi base addr
418         */
419         if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
420                 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
421                 goto exit;
422         }
423         /* if ACPI already enabled , do nothing */
424         if (!(b & 0x80) &&
425             pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
426                 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
427                 goto exit;
428         }
429
430         /* Determine the ACPI base address */
431         if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
432                 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
433                 goto exit;
434         }
435
436         dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
437
438         retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
439                                    sis630_driver.name);
440         if (retval)
441                 goto exit;
442
443         /* Everything is happy, let's grab the memory and set things up. */
444         if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
445                             sis630_driver.name)) {
446                 dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
447                         "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
448                 goto exit;
449         }
450
451         retval = 0;
452
453 exit:
454         if (retval)
455                 acpi_base = 0;
456         return retval;
457 }
458
459
460 static const struct i2c_algorithm smbus_algorithm = {
461         .smbus_xfer     = sis630_access,
462         .functionality  = sis630_func,
463 };
464
465 static struct i2c_adapter sis630_adapter = {
466         .owner          = THIS_MODULE,
467         .class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
468         .algo           = &smbus_algorithm,
469 };
470
471 static struct pci_device_id sis630_ids[] __devinitdata = {
472         { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
473         { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
474         { 0, }
475 };
476
477 MODULE_DEVICE_TABLE (pci, sis630_ids);
478
479 static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
480 {
481         if (sis630_setup(dev)) {
482                 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
483                 return -ENODEV;
484         }
485
486         /* set up the sysfs linkage to our parent device */
487         sis630_adapter.dev.parent = &dev->dev;
488
489         snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
490                  "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS);
491
492         return i2c_add_adapter(&sis630_adapter);
493 }
494
495 static void __devexit sis630_remove(struct pci_dev *dev)
496 {
497         if (acpi_base) {
498                 i2c_del_adapter(&sis630_adapter);
499                 release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
500                 acpi_base = 0;
501         }
502 }
503
504
505 static struct pci_driver sis630_driver = {
506         .name           = "sis630_smbus",
507         .id_table       = sis630_ids,
508         .probe          = sis630_probe,
509         .remove         = __devexit_p(sis630_remove),
510 };
511
512 static int __init i2c_sis630_init(void)
513 {
514         return pci_register_driver(&sis630_driver);
515 }
516
517
518 static void __exit i2c_sis630_exit(void)
519 {
520         pci_unregister_driver(&sis630_driver);
521 }
522
523
524 MODULE_LICENSE("GPL");
525 MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
526 MODULE_DESCRIPTION("SIS630 SMBus driver");
527
528 module_init(i2c_sis630_init);
529 module_exit(i2c_sis630_exit);