blob: df8e20ab3a09bf680ae1b52fc561262aab5a50a4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 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
Amaury Decrême974d6a32013-01-28 22:21:05 +010044 SIS 964
45
46 Notable differences between chips:
47 +------------------------+--------------------+-------------------+
48 | | SIS630/730 | SIS964 |
49 +------------------------+--------------------+-------------------+
50 | Clock | 14kHz/56kHz | 55.56kHz/27.78kHz |
51 | SMBus registers offset | 0x80 | 0xE0 |
52 | SMB_CNT | Bit 1 = Slave Busy | Bit 1 = Bus probe |
53 | (not used yet) | Bit 3 is reserved | Bit 3 = Last byte |
54 | SMB_PCOUNT | Offset + 0x06 | Offset + 0x14 |
55 | SMB_COUNT | 4:0 bits | 5:0 bits |
56 +------------------------+--------------------+-------------------+
57 (Other differences don't affect the functions provided by the driver)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59 Note: we assume there can only be one device, with one SMBus interface.
60*/
61
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#include <linux/kernel.h>
63#include <linux/module.h>
64#include <linux/delay.h>
65#include <linux/pci.h>
66#include <linux/ioport.h>
67#include <linux/init.h>
68#include <linux/i2c.h>
Jean Delvare54fb4a052008-07-14 22:38:33 +020069#include <linux/acpi.h>
H Hartley Sweeten21782182010-05-21 18:41:01 +020070#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Amaury Decrême974d6a32013-01-28 22:21:05 +010072/* SIS964 id is defined here as we are the only file using it */
73#define PCI_DEVICE_ID_SI_964 0x0964
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Amaury Decrême974d6a32013-01-28 22:21:05 +010075/* SIS630/730/964 SMBus registers */
76#define SMB_STS 0x00 /* status */
77#define SMB_CNT 0x02 /* control */
78#define SMBHOST_CNT 0x03 /* host control */
79#define SMB_ADDR 0x04 /* address */
80#define SMB_CMD 0x05 /* command */
81#define SMB_COUNT 0x07 /* byte count */
82#define SMB_BYTE 0x08 /* ~0x8F data byte field */
83
84/* register count for request_region
85 * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964
86 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070087#define SIS630_SMB_IOREGION 20
88
89/* PCI address constants */
90/* acpi base address register */
91#define SIS630_ACPI_BASE_REG 0x74
92/* bios control register */
93#define SIS630_BIOS_CTL_REG 0x40
94
95/* Other settings */
96#define MAX_TIMEOUT 500
97
98/* SIS630 constants */
99#define SIS630_QUICK 0x00
100#define SIS630_BYTE 0x01
101#define SIS630_BYTE_DATA 0x02
102#define SIS630_WORD_DATA 0x03
103#define SIS630_PCALL 0x04
104#define SIS630_BLOCK_DATA 0x05
105
Jean Delvared6072f82005-09-25 16:37:04 +0200106static struct pci_driver sis630_driver;
107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/* insmod parameters */
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030109static bool high_clock;
110static bool force;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111module_param(high_clock, bool, 0);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100112MODULE_PARM_DESC(high_clock,
113 "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only).");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114module_param(force, bool, 0);
115MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
116
Amaury Decrême974d6a32013-01-28 22:21:05 +0100117/* SMBus base adress */
118static unsigned short smbus_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120/* supported chips */
121static int supported[] = {
122 PCI_DEVICE_ID_SI_630,
123 PCI_DEVICE_ID_SI_730,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100124 PCI_DEVICE_ID_SI_760,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 0 /* terminates the list */
126};
127
128static inline u8 sis630_read(u8 reg)
129{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100130 return inb(smbus_base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131}
132
133static inline void sis630_write(u8 reg, u8 data)
134{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100135 outb(data, smbus_base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
138static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
139{
140 int temp;
141
142 /* Make sure the SMBus host is ready to start transmitting. */
143 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
144 dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
145 /* kill smbus transaction */
146 sis630_write(SMBHOST_CNT, 0x20);
147
148 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
149 dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
David Brownell97140342008-07-14 22:38:25 +0200150 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 } else {
Jean Delvarec5d21b72008-04-29 23:11:37 +0200152 dev_dbg(&adap->dev, "Successful!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 }
154 }
155
156 /* save old clock, so we can prevent machine for hung */
157 *oldclock = sis630_read(SMB_CNT);
158
159 dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
160
161 /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
162 if (high_clock)
163 sis630_write(SMB_CNT, 0x20);
164 else
165 sis630_write(SMB_CNT, (*oldclock & ~0x40));
166
167 /* clear all sticky bits */
168 temp = sis630_read(SMB_STS);
169 sis630_write(SMB_STS, temp & 0x1e);
170
171 /* start the transaction by setting bit 4 and size */
172 sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
173
174 return 0;
175}
176
177static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
178{
179 int temp, result = 0, timeout = 0;
180
181 /* We will always wait for a fraction of a second! */
182 do {
183 msleep(1);
184 temp = sis630_read(SMB_STS);
185 /* check if block transmitted */
186 if (size == SIS630_BLOCK_DATA && (temp & 0x10))
187 break;
188 } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
189
190 /* If the SMBus is still busy, we give up */
Roel Kluin4ccc28f2009-05-05 08:39:24 +0200191 if (timeout > MAX_TIMEOUT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 dev_dbg(&adap->dev, "SMBus Timeout!\n");
David Brownell97140342008-07-14 22:38:25 +0200193 result = -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 }
195
196 if (temp & 0x02) {
197 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
David Brownell97140342008-07-14 22:38:25 +0200198 result = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 }
200
201 if (temp & 0x04) {
202 dev_err(&adap->dev, "Bus collision!\n");
David Brownell97140342008-07-14 22:38:25 +0200203 result = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 /*
205 TBD: Datasheet say:
206 the software should clear this bit and restart SMBUS operation.
207 Should we do it or user start request again?
208 */
209 }
210
211 return result;
212}
213
214static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
215{
216 int temp = 0;
217
218 /* clear all status "sticky" bits */
219 sis630_write(SMB_STS, temp);
220
221 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
222
223 /*
224 * restore old Host Master Clock if high_clock is set
225 * and oldclock was not 56KHz
226 */
227 if (high_clock && !(oldclock & 0x20))
228 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
229
230 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
231}
232
233static int sis630_transaction(struct i2c_adapter *adap, int size)
234{
235 int result = 0;
236 u8 oldclock = 0;
237
238 result = sis630_transaction_start(adap, size, &oldclock);
239 if (!result) {
240 result = sis630_transaction_wait(adap, size);
241 sis630_transaction_end(adap, oldclock);
242 }
243
244 return result;
245}
246
247static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
248{
249 int i, len = 0, rc = 0;
250 u8 oldclock = 0;
251
252 if (read_write == I2C_SMBUS_WRITE) {
253 len = data->block[0];
254 if (len < 0)
255 len = 0;
256 else if (len > 32)
257 len = 32;
258 sis630_write(SMB_COUNT, len);
259 for (i=1; i <= len; i++) {
260 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
261 /* set data */
262 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
263 if (i==8 || (len<8 && i==len)) {
264 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
265 /* first transaction */
David Brownell97140342008-07-14 22:38:25 +0200266 rc = sis630_transaction_start(adap,
267 SIS630_BLOCK_DATA, &oldclock);
268 if (rc)
269 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 }
271 else if ((i-1)%8 == 7 || i==len) {
272 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
273 if (i>8) {
274 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
275 /*
276 If this is not first transaction,
277 we must clear sticky bit.
278 clear SMBARY_STS
279 */
280 sis630_write(SMB_STS,0x10);
281 }
David Brownell97140342008-07-14 22:38:25 +0200282 rc = sis630_transaction_wait(adap,
283 SIS630_BLOCK_DATA);
284 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 break;
287 }
288 }
289 }
290 }
291 else {
292 /* read request */
293 data->block[0] = len = 0;
David Brownell97140342008-07-14 22:38:25 +0200294 rc = sis630_transaction_start(adap,
295 SIS630_BLOCK_DATA, &oldclock);
296 if (rc)
297 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 do {
David Brownell97140342008-07-14 22:38:25 +0200299 rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
300 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 break;
303 }
304 /* if this first transaction then read byte count */
305 if (len == 0)
306 data->block[0] = sis630_read(SMB_COUNT);
307
308 /* just to be sure */
309 if (data->block[0] > 32)
310 data->block[0] = 32;
311
312 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
313
314 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
315 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
316 data->block[len+1] = sis630_read(SMB_BYTE+i);
317 }
318
319 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
320
321 /* clear SMBARY_STS */
322 sis630_write(SMB_STS,0x10);
323 } while(len < data->block[0]);
324 }
325
326 sis630_transaction_end(adap, oldclock);
327
328 return rc;
329}
330
David Brownell97140342008-07-14 22:38:25 +0200331/* Return negative errno on error. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
333 unsigned short flags, char read_write,
334 u8 command, int size, union i2c_smbus_data *data)
335{
David Brownell97140342008-07-14 22:38:25 +0200336 int status;
337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 switch (size) {
339 case I2C_SMBUS_QUICK:
340 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
341 size = SIS630_QUICK;
342 break;
343 case I2C_SMBUS_BYTE:
344 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
345 if (read_write == I2C_SMBUS_WRITE)
346 sis630_write(SMB_CMD, command);
347 size = SIS630_BYTE;
348 break;
349 case I2C_SMBUS_BYTE_DATA:
350 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
351 sis630_write(SMB_CMD, command);
352 if (read_write == I2C_SMBUS_WRITE)
353 sis630_write(SMB_BYTE, data->byte);
354 size = SIS630_BYTE_DATA;
355 break;
356 case I2C_SMBUS_PROC_CALL:
357 case I2C_SMBUS_WORD_DATA:
358 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
359 sis630_write(SMB_CMD, command);
360 if (read_write == I2C_SMBUS_WRITE) {
361 sis630_write(SMB_BYTE, data->word & 0xff);
362 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
363 }
364 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
365 break;
366 case I2C_SMBUS_BLOCK_DATA:
367 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
368 sis630_write(SMB_CMD, command);
369 size = SIS630_BLOCK_DATA;
370 return sis630_block_data(adap, data, read_write);
371 default:
Jean Delvareac7fc4f2008-07-14 22:38:25 +0200372 dev_warn(&adap->dev, "Unsupported transaction %d\n",
373 size);
David Brownell97140342008-07-14 22:38:25 +0200374 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 }
376
David Brownell97140342008-07-14 22:38:25 +0200377 status = sis630_transaction(adap, size);
378 if (status)
379 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 if ((size != SIS630_PCALL) &&
382 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
383 return 0;
384 }
385
386 switch(size) {
387 case SIS630_BYTE:
388 case SIS630_BYTE_DATA:
389 data->byte = sis630_read(SMB_BYTE);
390 break;
391 case SIS630_PCALL:
392 case SIS630_WORD_DATA:
393 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
394 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396
397 return 0;
398}
399
400static u32 sis630_func(struct i2c_adapter *adapter)
401{
402 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
403 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
404 I2C_FUNC_SMBUS_BLOCK_DATA;
405}
406
Bill Pemberton0b255e92012-11-27 15:59:38 -0500407static int sis630_setup(struct pci_dev *sis630_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 unsigned char b;
410 struct pci_dev *dummy = NULL;
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100411 int retval, i;
Amaury Decrême974d6a32013-01-28 22:21:05 +0100412 /* acpi base address */
413 unsigned short acpi_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415 /* check for supported SiS devices */
416 for (i=0; supported[i] > 0 ; i++) {
417 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
418 break; /* found */
419 }
420
421 if (dummy) {
422 pci_dev_put(dummy);
423 }
424 else if (force) {
425 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
426 "loading because of force option enabled\n");
427 }
428 else {
429 return -ENODEV;
430 }
431
432 /*
433 Enable ACPI first , so we can accsess reg 74-75
434 in acpi io space and read acpi base addr
435 */
436 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
437 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100438 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 goto exit;
440 }
441 /* if ACPI already enabled , do nothing */
442 if (!(b & 0x80) &&
443 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
444 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100445 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 goto exit;
447 }
448
449 /* Determine the ACPI base address */
450 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
451 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100452 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 goto exit;
454 }
455
456 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
457
Amaury Decrême974d6a32013-01-28 22:21:05 +0100458 if (supported[i] == PCI_DEVICE_ID_SI_760)
459 smbus_base = acpi_base + 0xE0;
460 else
461 smbus_base = acpi_base + 0x80;
462
463 dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base);
464
465 retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvare54fb4a052008-07-14 22:38:33 +0200466 sis630_driver.name);
467 if (retval)
468 goto exit;
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 /* Everything is happy, let's grab the memory and set things up. */
Amaury Decrême974d6a32013-01-28 22:21:05 +0100471 if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvared6072f82005-09-25 16:37:04 +0200472 sis630_driver.name)) {
Amaury Decrême974d6a32013-01-28 22:21:05 +0100473 dev_err(&sis630_dev->dev,
474 "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
475 smbus_base + SMB_STS,
476 smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100477 retval = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 goto exit;
479 }
480
481 retval = 0;
482
483exit:
484 if (retval)
Amaury Decrême974d6a32013-01-28 22:21:05 +0100485 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 return retval;
487}
488
489
Jean Delvare8f9082c2006-09-03 22:39:46 +0200490static const struct i2c_algorithm smbus_algorithm = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 .smbus_xfer = sis630_access,
492 .functionality = sis630_func,
493};
494
495static struct i2c_adapter sis630_adapter = {
496 .owner = THIS_MODULE,
Jean Delvare3401b2f2008-07-14 22:38:29 +0200497 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 .algo = &smbus_algorithm,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100499 .retries = 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500};
501
Axel Lin3527bd52012-01-12 20:32:04 +0100502static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
504 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
Amaury Decrême974d6a32013-01-28 22:21:05 +0100505 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 { 0, }
507};
508
509MODULE_DEVICE_TABLE (pci, sis630_ids);
510
Bill Pemberton0b255e92012-11-27 15:59:38 -0500511static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512{
513 if (sis630_setup(dev)) {
514 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
515 return -ENODEV;
516 }
517
Robert P. J. Day405ae7d2007-02-17 19:13:42 +0100518 /* set up the sysfs linkage to our parent device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 sis630_adapter.dev.parent = &dev->dev;
520
Jean Delvare66c7acf2009-01-07 14:29:18 +0100521 snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
Amaury Decrême974d6a32013-01-28 22:21:05 +0100522 "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
524 return i2c_add_adapter(&sis630_adapter);
525}
526
Bill Pemberton0b255e92012-11-27 15:59:38 -0500527static void sis630_remove(struct pci_dev *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100529 if (smbus_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 i2c_del_adapter(&sis630_adapter);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100531 release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
532 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 }
534}
535
536
537static struct pci_driver sis630_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 .name = "sis630_smbus",
539 .id_table = sis630_ids,
540 .probe = sis630_probe,
Bill Pemberton0b255e92012-11-27 15:59:38 -0500541 .remove = sis630_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542};
543
Axel Lin56f21782012-07-24 14:13:56 +0200544module_pci_driver(sis630_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
546MODULE_LICENSE("GPL");
547MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
548MODULE_DESCRIPTION("SIS630 SMBus driver");