blob: 3124d807c05a528452c0e2c9a9188caa6ae7f677 [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{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 /* clear all status "sticky" bits */
Amaury Decrêmeaa9e7a32013-01-28 22:21:06 +0100217 sis630_write(SMB_STS, 0xFF);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
220
221 /*
222 * restore old Host Master Clock if high_clock is set
223 * and oldclock was not 56KHz
224 */
225 if (high_clock && !(oldclock & 0x20))
226 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
227
228 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
229}
230
231static int sis630_transaction(struct i2c_adapter *adap, int size)
232{
233 int result = 0;
234 u8 oldclock = 0;
235
236 result = sis630_transaction_start(adap, size, &oldclock);
237 if (!result) {
238 result = sis630_transaction_wait(adap, size);
239 sis630_transaction_end(adap, oldclock);
240 }
241
242 return result;
243}
244
245static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
246{
247 int i, len = 0, rc = 0;
248 u8 oldclock = 0;
249
250 if (read_write == I2C_SMBUS_WRITE) {
251 len = data->block[0];
252 if (len < 0)
253 len = 0;
254 else if (len > 32)
255 len = 32;
256 sis630_write(SMB_COUNT, len);
257 for (i=1; i <= len; i++) {
258 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
259 /* set data */
260 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
261 if (i==8 || (len<8 && i==len)) {
262 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
263 /* first transaction */
David Brownell97140342008-07-14 22:38:25 +0200264 rc = sis630_transaction_start(adap,
265 SIS630_BLOCK_DATA, &oldclock);
266 if (rc)
267 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 }
269 else if ((i-1)%8 == 7 || i==len) {
270 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
271 if (i>8) {
272 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
273 /*
274 If this is not first transaction,
275 we must clear sticky bit.
276 clear SMBARY_STS
277 */
278 sis630_write(SMB_STS,0x10);
279 }
David Brownell97140342008-07-14 22:38:25 +0200280 rc = sis630_transaction_wait(adap,
281 SIS630_BLOCK_DATA);
282 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 break;
285 }
286 }
287 }
288 }
289 else {
290 /* read request */
291 data->block[0] = len = 0;
David Brownell97140342008-07-14 22:38:25 +0200292 rc = sis630_transaction_start(adap,
293 SIS630_BLOCK_DATA, &oldclock);
294 if (rc)
295 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 do {
David Brownell97140342008-07-14 22:38:25 +0200297 rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
298 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 break;
301 }
302 /* if this first transaction then read byte count */
303 if (len == 0)
304 data->block[0] = sis630_read(SMB_COUNT);
305
306 /* just to be sure */
307 if (data->block[0] > 32)
308 data->block[0] = 32;
309
310 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
311
312 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
313 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
314 data->block[len+1] = sis630_read(SMB_BYTE+i);
315 }
316
317 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
318
319 /* clear SMBARY_STS */
320 sis630_write(SMB_STS,0x10);
321 } while(len < data->block[0]);
322 }
323
324 sis630_transaction_end(adap, oldclock);
325
326 return rc;
327}
328
David Brownell97140342008-07-14 22:38:25 +0200329/* Return negative errno on error. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
331 unsigned short flags, char read_write,
332 u8 command, int size, union i2c_smbus_data *data)
333{
David Brownell97140342008-07-14 22:38:25 +0200334 int status;
335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 switch (size) {
337 case I2C_SMBUS_QUICK:
338 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
339 size = SIS630_QUICK;
340 break;
341 case I2C_SMBUS_BYTE:
342 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
343 if (read_write == I2C_SMBUS_WRITE)
344 sis630_write(SMB_CMD, command);
345 size = SIS630_BYTE;
346 break;
347 case I2C_SMBUS_BYTE_DATA:
348 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
349 sis630_write(SMB_CMD, command);
350 if (read_write == I2C_SMBUS_WRITE)
351 sis630_write(SMB_BYTE, data->byte);
352 size = SIS630_BYTE_DATA;
353 break;
354 case I2C_SMBUS_PROC_CALL:
355 case I2C_SMBUS_WORD_DATA:
356 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
357 sis630_write(SMB_CMD, command);
358 if (read_write == I2C_SMBUS_WRITE) {
359 sis630_write(SMB_BYTE, data->word & 0xff);
360 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
361 }
362 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
363 break;
364 case I2C_SMBUS_BLOCK_DATA:
365 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
366 sis630_write(SMB_CMD, command);
367 size = SIS630_BLOCK_DATA;
368 return sis630_block_data(adap, data, read_write);
369 default:
Jean Delvareac7fc4f2008-07-14 22:38:25 +0200370 dev_warn(&adap->dev, "Unsupported transaction %d\n",
371 size);
David Brownell97140342008-07-14 22:38:25 +0200372 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 }
374
David Brownell97140342008-07-14 22:38:25 +0200375 status = sis630_transaction(adap, size);
376 if (status)
377 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 if ((size != SIS630_PCALL) &&
380 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
381 return 0;
382 }
383
384 switch(size) {
385 case SIS630_BYTE:
386 case SIS630_BYTE_DATA:
387 data->byte = sis630_read(SMB_BYTE);
388 break;
389 case SIS630_PCALL:
390 case SIS630_WORD_DATA:
391 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
392 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 }
394
395 return 0;
396}
397
398static u32 sis630_func(struct i2c_adapter *adapter)
399{
400 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
401 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
402 I2C_FUNC_SMBUS_BLOCK_DATA;
403}
404
Bill Pemberton0b255e92012-11-27 15:59:38 -0500405static int sis630_setup(struct pci_dev *sis630_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406{
407 unsigned char b;
408 struct pci_dev *dummy = NULL;
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100409 int retval, i;
Amaury Decrême974d6a32013-01-28 22:21:05 +0100410 /* acpi base address */
411 unsigned short acpi_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
413 /* check for supported SiS devices */
414 for (i=0; supported[i] > 0 ; i++) {
415 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
416 break; /* found */
417 }
418
419 if (dummy) {
420 pci_dev_put(dummy);
421 }
422 else if (force) {
423 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
424 "loading because of force option enabled\n");
425 }
426 else {
427 return -ENODEV;
428 }
429
430 /*
431 Enable ACPI first , so we can accsess reg 74-75
432 in acpi io space and read acpi base addr
433 */
434 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
435 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100436 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 goto exit;
438 }
439 /* if ACPI already enabled , do nothing */
440 if (!(b & 0x80) &&
441 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
442 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100443 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 goto exit;
445 }
446
447 /* Determine the ACPI base address */
448 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
449 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100450 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 goto exit;
452 }
453
454 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
455
Amaury Decrême974d6a32013-01-28 22:21:05 +0100456 if (supported[i] == PCI_DEVICE_ID_SI_760)
457 smbus_base = acpi_base + 0xE0;
458 else
459 smbus_base = acpi_base + 0x80;
460
461 dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base);
462
463 retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvare54fb4a052008-07-14 22:38:33 +0200464 sis630_driver.name);
465 if (retval)
466 goto exit;
467
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 /* Everything is happy, let's grab the memory and set things up. */
Amaury Decrême974d6a32013-01-28 22:21:05 +0100469 if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvared6072f82005-09-25 16:37:04 +0200470 sis630_driver.name)) {
Amaury Decrême974d6a32013-01-28 22:21:05 +0100471 dev_err(&sis630_dev->dev,
472 "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
473 smbus_base + SMB_STS,
474 smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100475 retval = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 goto exit;
477 }
478
479 retval = 0;
480
481exit:
482 if (retval)
Amaury Decrême974d6a32013-01-28 22:21:05 +0100483 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 return retval;
485}
486
487
Jean Delvare8f9082c2006-09-03 22:39:46 +0200488static const struct i2c_algorithm smbus_algorithm = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 .smbus_xfer = sis630_access,
490 .functionality = sis630_func,
491};
492
493static struct i2c_adapter sis630_adapter = {
494 .owner = THIS_MODULE,
Jean Delvare3401b2f2008-07-14 22:38:29 +0200495 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 .algo = &smbus_algorithm,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100497 .retries = 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498};
499
Axel Lin3527bd52012-01-12 20:32:04 +0100500static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
502 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
Amaury Decrême974d6a32013-01-28 22:21:05 +0100503 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 { 0, }
505};
506
507MODULE_DEVICE_TABLE (pci, sis630_ids);
508
Bill Pemberton0b255e92012-11-27 15:59:38 -0500509static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
511 if (sis630_setup(dev)) {
512 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
513 return -ENODEV;
514 }
515
Robert P. J. Day405ae7d2007-02-17 19:13:42 +0100516 /* set up the sysfs linkage to our parent device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 sis630_adapter.dev.parent = &dev->dev;
518
Jean Delvare66c7acf2009-01-07 14:29:18 +0100519 snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
Amaury Decrême974d6a32013-01-28 22:21:05 +0100520 "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 return i2c_add_adapter(&sis630_adapter);
523}
524
Bill Pemberton0b255e92012-11-27 15:59:38 -0500525static void sis630_remove(struct pci_dev *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100527 if (smbus_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 i2c_del_adapter(&sis630_adapter);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100529 release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
530 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 }
532}
533
534
535static struct pci_driver sis630_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 .name = "sis630_smbus",
537 .id_table = sis630_ids,
538 .probe = sis630_probe,
Bill Pemberton0b255e92012-11-27 15:59:38 -0500539 .remove = sis630_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540};
541
Axel Lin56f21782012-07-24 14:13:56 +0200542module_pci_driver(sis630_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
544MODULE_LICENSE("GPL");
545MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
546MODULE_DESCRIPTION("SIS630 SMBus driver");