Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  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 
 51 #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 static struct pci_driver sis630_driver;
 96 
 97 /* insmod parameters */
 98 static int high_clock;
 99 static int force;
100 module_param(high_clock, bool, 0);
101 MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
102 module_param(force, bool, 0);
103 MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
104 
105 /* acpi base address */
106 static unsigned short acpi_base;
107 
108 /* supported chips */
109 static int supported[] = {
110         PCI_DEVICE_ID_SI_630,
111         PCI_DEVICE_ID_SI_730,
112         0 /* terminates the list */
113 };
114 
115 static inline u8 sis630_read(u8 reg)
116 {
117         return inb(acpi_base + reg);
118 }
119 
120 static inline void sis630_write(u8 reg, u8 data)
121 {
122         outb(data, acpi_base + reg);
123 }
124 
125 static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
126 {
127         int temp;
128 
129         /* Make sure the SMBus host is ready to start transmitting. */
130         if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
131                 dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
132                 /* kill smbus transaction */
133                 sis630_write(SMBHOST_CNT, 0x20);
134 
135                 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
136                         dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
137                         return -1;
138                 } else {
139                         dev_dbg(&adap->dev, "Successfull!\n");
140                 }
141         }
142 
143         /* save old clock, so we can prevent machine for hung */
144         *oldclock = sis630_read(SMB_CNT);
145 
146         dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
147 
148         /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
149         if (high_clock)
150                 sis630_write(SMB_CNT, 0x20);
151         else
152                 sis630_write(SMB_CNT, (*oldclock & ~0x40));
153 
154         /* clear all sticky bits */
155         temp = sis630_read(SMB_STS);
156         sis630_write(SMB_STS, temp & 0x1e);
157 
158         /* start the transaction by setting bit 4 and size */
159         sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
160 
161         return 0;
162 }
163 
164 static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
165 {
166         int temp, result = 0, timeout = 0;
167 
168         /* We will always wait for a fraction of a second! */
169         do {
170                 msleep(1);
171                 temp = sis630_read(SMB_STS);
172                 /* check if block transmitted */
173                 if (size == SIS630_BLOCK_DATA && (temp & 0x10))
174                         break;
175         } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
176 
177         /* If the SMBus is still busy, we give up */
178         if (timeout >= MAX_TIMEOUT) {
179                 dev_dbg(&adap->dev, "SMBus Timeout!\n");
180                 result = -1;
181         }
182 
183         if (temp & 0x02) {
184                 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
185                 result = -1;
186         }
187 
188         if (temp & 0x04) {
189                 dev_err(&adap->dev, "Bus collision!\n");
190                 result = -1;
191                 /*
192                   TBD: Datasheet say:
193                   the software should clear this bit and restart SMBUS operation.
194                   Should we do it or user start request again?
195                 */
196         }
197 
198         return result;
199 }
200 
201 static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
202 {
203         int temp = 0;
204 
205         /* clear all status "sticky" bits */
206         sis630_write(SMB_STS, temp);
207 
208         dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
209 
210         /*
211          * restore old Host Master Clock if high_clock is set
212          * and oldclock was not 56KHz
213          */
214         if (high_clock && !(oldclock & 0x20))
215                 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
216 
217         dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
218 }
219 
220 static int sis630_transaction(struct i2c_adapter *adap, int size)
221 {
222         int result = 0;
223         u8 oldclock = 0;
224 
225         result = sis630_transaction_start(adap, size, &oldclock);
226         if (!result) {
227                 result = sis630_transaction_wait(adap, size);
228                 sis630_transaction_end(adap, oldclock);
229         }
230 
231         return result;
232 }
233 
234 static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
235 {
236         int i, len = 0, rc = 0;
237         u8 oldclock = 0;
238 
239         if (read_write == I2C_SMBUS_WRITE) {
240                 len = data->block[0];
241                 if (len < 0)
242                         len = 0;
243                 else if (len > 32)
244                         len = 32;
245                 sis630_write(SMB_COUNT, len);
246                 for (i=1; i <= len; i++) {
247                         dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
248                         /* set data */
249                         sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
250                         if (i==8 || (len<8 && i==len)) {
251                                 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
252                                 /* first transaction */
253                                 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock))
254                                         return -1;
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                                 if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
268                                         dev_dbg(&adap->dev, "trans_wait failed\n");
269                                         rc = -1;
270                                         break;
271                                 }
272                         }
273                 }
274         }
275         else {
276                 /* read request */
277                 data->block[0] = len = 0;
278                 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock)) {
279                         return -1;
280                 }
281                 do {
282                         if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
283                                 dev_dbg(&adap->dev, "trans_wait failed\n");
284                                 rc = -1;
285                                 break;
286                         }
287                         /* if this first transaction then read byte count */
288                         if (len == 0)
289                                 data->block[0] = sis630_read(SMB_COUNT);
290 
291                         /* just to be sure */
292                         if (data->block[0] > 32)
293                                 data->block[0] = 32;
294 
295                         dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
296 
297                         for (i=0; i < 8 && len < data->block[0]; i++,len++) {
298                                 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
299                                 data->block[len+1] = sis630_read(SMB_BYTE+i);
300                         }
301 
302                         dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
303 
304                         /* clear SMBARY_STS */
305                         sis630_write(SMB_STS,0x10);
306                 } while(len < data->block[0]);
307         }
308 
309         sis630_transaction_end(adap, oldclock);
310 
311         return rc;
312 }
313 
314 /* Return -1 on error. */
315 static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
316                          unsigned short flags, char read_write,
317                          u8 command, int size, union i2c_smbus_data *data)
318 {
319         switch (size) {
320                 case I2C_SMBUS_QUICK:
321                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
322                         size = SIS630_QUICK;
323                         break;
324                 case I2C_SMBUS_BYTE:
325                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
326                         if (read_write == I2C_SMBUS_WRITE)
327                                 sis630_write(SMB_CMD, command);
328                         size = SIS630_BYTE;
329                         break;
330                 case I2C_SMBUS_BYTE_DATA:
331                         sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
332                         sis630_write(SMB_CMD, command);
333                         if (read_write == I2C_SMBUS_WRITE)
334                                 sis630_write(SMB_BYTE, data->byte);
335                         size = SIS630_BYTE_DATA;
336                         break;
337                 case I2C_SMBUS_PROC_CALL:
338                 case I2C_SMBUS_WORD_DATA:
339                         sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
340                         sis630_write(SMB_CMD, command);
341                         if (read_write == I2C_SMBUS_WRITE) {
342                                 sis630_write(SMB_BYTE, data->word & 0xff);
343                                 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
344                         }
345                         size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
346                         break;
347                 case I2C_SMBUS_BLOCK_DATA:
348                         sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
349                         sis630_write(SMB_CMD, command);
350                         size = SIS630_BLOCK_DATA;
351                         return sis630_block_data(adap, data, read_write);
352                 default:
353                         printk("Unsupported I2C size\n");
354                         return -1;
355                         break;
356         }
357 
358         if (sis630_transaction(adap, size))
359                 return -1;
360 
361         if ((size != SIS630_PCALL) &&
362                 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
363                 return 0;
364         }
365 
366         switch(size) {
367                 case SIS630_BYTE:
368                 case SIS630_BYTE_DATA:
369                         data->byte = sis630_read(SMB_BYTE);
370                         break;
371                 case SIS630_PCALL:
372                 case SIS630_WORD_DATA:
373                         data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
374                         break;
375                 default:
376                         return -1;
377                         break;
378         }
379 
380         return 0;
381 }
382 
383 static u32 sis630_func(struct i2c_adapter *adapter)
384 {
385         return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
386                 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
387                 I2C_FUNC_SMBUS_BLOCK_DATA;
388 }
389 
390 static int sis630_setup(struct pci_dev *sis630_dev)
391 {
392         unsigned char b;
393         struct pci_dev *dummy = NULL;
394         int retval = -ENODEV, i;
395 
396         /* check for supported SiS devices */
397         for (i=0; supported[i] > 0 ; i++) {
398                 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
399                         break; /* found */
400         }
401 
402         if (dummy) {
403                 pci_dev_put(dummy);
404         }
405         else if (force) {
406                 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
407                         "loading because of force option enabled\n");
408         }
409         else {
410                 return -ENODEV;
411         }
412 
413         /*
414            Enable ACPI first , so we can accsess reg 74-75
415            in acpi io space and read acpi base addr
416         */
417         if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
418                 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
419                 goto exit;
420         }
421         /* if ACPI already enabled , do nothing */
422         if (!(b & 0x80) &&
423             pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
424                 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
425                 goto exit;
426         }
427 
428         /* Determine the ACPI base address */
429         if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
430                 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
431                 goto exit;
432         }
433 
434         dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
435 
436         /* Everything is happy, let's grab the memory and set things up. */
437         if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
438                             sis630_driver.name)) {
439                 dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
440                         "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
441                 goto exit;
442         }
443 
444         retval = 0;
445 
446 exit:
447         if (retval)
448                 acpi_base = 0;
449         return retval;
450 }
451 
452 
453 static const struct i2c_algorithm smbus_algorithm = {
454         .smbus_xfer     = sis630_access,
455         .functionality  = sis630_func,
456 };
457 
458 static struct i2c_adapter sis630_adapter = {
459         .owner          = THIS_MODULE,
460         .id             = I2C_HW_SMBUS_SIS630,
461         .class          = I2C_CLASS_HWMON,
462         .algo           = &smbus_algorithm,
463 };
464 
465 static struct pci_device_id sis630_ids[] __devinitdata = {
466         { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
467         { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
468         { 0, }
469 };
470 
471 MODULE_DEVICE_TABLE (pci, sis630_ids);
472 
473 static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
474 {
475         if (sis630_setup(dev)) {
476                 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
477                 return -ENODEV;
478         }
479 
480         /* set up the sysfs linkage to our parent device */
481         sis630_adapter.dev.parent = &dev->dev;
482 
483         sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
484                 acpi_base + SMB_STS);
485 
486         return i2c_add_adapter(&sis630_adapter);
487 }
488 
489 static void __devexit sis630_remove(struct pci_dev *dev)
490 {
491         if (acpi_base) {
492                 i2c_del_adapter(&sis630_adapter);
493                 release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
494                 acpi_base = 0;
495         }
496 }
497 
498 
499 static struct pci_driver sis630_driver = {
500         .name           = "sis630_smbus",
501         .id_table       = sis630_ids,
502         .probe          = sis630_probe,
503         .remove         = __devexit_p(sis630_remove),
504 };
505 
506 static int __init i2c_sis630_init(void)
507 {
508         return pci_register_driver(&sis630_driver);
509 }
510 
511 
512 static void __exit i2c_sis630_exit(void)
513 {
514         pci_unregister_driver(&sis630_driver);
515 }
516 
517 
518 MODULE_LICENSE("GPL");
519 MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
520 MODULE_DESCRIPTION("SIS630 SMBus driver");
521 
522 module_init(i2c_sis630_init);
523 module_exit(i2c_sis630_exit);
524 
  This page was automatically generated by the LXR engine.