blob: e152d36b94ac310330ebf5976ab1256df5211175 [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");
Amaury Decrême499b9192013-01-28 22:21:07 +0100203 result = -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
205
206 return result;
207}
208
209static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
210{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 /* clear all status "sticky" bits */
Amaury Decrêmeaa9e7a32013-01-28 22:21:06 +0100212 sis630_write(SMB_STS, 0xFF);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
214 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
215
216 /*
217 * restore old Host Master Clock if high_clock is set
218 * and oldclock was not 56KHz
219 */
220 if (high_clock && !(oldclock & 0x20))
221 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
222
223 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
224}
225
226static int sis630_transaction(struct i2c_adapter *adap, int size)
227{
228 int result = 0;
229 u8 oldclock = 0;
230
231 result = sis630_transaction_start(adap, size, &oldclock);
232 if (!result) {
233 result = sis630_transaction_wait(adap, size);
234 sis630_transaction_end(adap, oldclock);
235 }
236
237 return result;
238}
239
240static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
241{
242 int i, len = 0, rc = 0;
243 u8 oldclock = 0;
244
245 if (read_write == I2C_SMBUS_WRITE) {
246 len = data->block[0];
247 if (len < 0)
248 len = 0;
249 else if (len > 32)
250 len = 32;
251 sis630_write(SMB_COUNT, len);
252 for (i=1; i <= len; i++) {
253 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
254 /* set data */
255 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
256 if (i==8 || (len<8 && i==len)) {
257 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
258 /* first transaction */
David Brownell97140342008-07-14 22:38:25 +0200259 rc = sis630_transaction_start(adap,
260 SIS630_BLOCK_DATA, &oldclock);
261 if (rc)
262 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
264 else if ((i-1)%8 == 7 || i==len) {
265 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
266 if (i>8) {
267 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
268 /*
269 If this is not first transaction,
270 we must clear sticky bit.
271 clear SMBARY_STS
272 */
273 sis630_write(SMB_STS,0x10);
274 }
David Brownell97140342008-07-14 22:38:25 +0200275 rc = sis630_transaction_wait(adap,
276 SIS630_BLOCK_DATA);
277 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 break;
280 }
281 }
282 }
283 }
284 else {
285 /* read request */
286 data->block[0] = len = 0;
David Brownell97140342008-07-14 22:38:25 +0200287 rc = sis630_transaction_start(adap,
288 SIS630_BLOCK_DATA, &oldclock);
289 if (rc)
290 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 do {
David Brownell97140342008-07-14 22:38:25 +0200292 rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
293 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 break;
296 }
297 /* if this first transaction then read byte count */
298 if (len == 0)
299 data->block[0] = sis630_read(SMB_COUNT);
300
301 /* just to be sure */
302 if (data->block[0] > 32)
303 data->block[0] = 32;
304
305 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
306
307 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
308 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
309 data->block[len+1] = sis630_read(SMB_BYTE+i);
310 }
311
312 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
313
314 /* clear SMBARY_STS */
315 sis630_write(SMB_STS,0x10);
316 } while(len < data->block[0]);
317 }
318
319 sis630_transaction_end(adap, oldclock);
320
321 return rc;
322}
323
David Brownell97140342008-07-14 22:38:25 +0200324/* Return negative errno on error. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
326 unsigned short flags, char read_write,
327 u8 command, int size, union i2c_smbus_data *data)
328{
David Brownell97140342008-07-14 22:38:25 +0200329 int status;
330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 switch (size) {
332 case I2C_SMBUS_QUICK:
333 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
334 size = SIS630_QUICK;
335 break;
336 case I2C_SMBUS_BYTE:
337 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
338 if (read_write == I2C_SMBUS_WRITE)
339 sis630_write(SMB_CMD, command);
340 size = SIS630_BYTE;
341 break;
342 case I2C_SMBUS_BYTE_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->byte);
347 size = SIS630_BYTE_DATA;
348 break;
349 case I2C_SMBUS_PROC_CALL:
350 case I2C_SMBUS_WORD_DATA:
351 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
352 sis630_write(SMB_CMD, command);
353 if (read_write == I2C_SMBUS_WRITE) {
354 sis630_write(SMB_BYTE, data->word & 0xff);
355 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
356 }
357 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
358 break;
359 case I2C_SMBUS_BLOCK_DATA:
360 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
361 sis630_write(SMB_CMD, command);
362 size = SIS630_BLOCK_DATA;
363 return sis630_block_data(adap, data, read_write);
364 default:
Jean Delvareac7fc4f2008-07-14 22:38:25 +0200365 dev_warn(&adap->dev, "Unsupported transaction %d\n",
366 size);
David Brownell97140342008-07-14 22:38:25 +0200367 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 }
369
David Brownell97140342008-07-14 22:38:25 +0200370 status = sis630_transaction(adap, size);
371 if (status)
372 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
374 if ((size != SIS630_PCALL) &&
375 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
376 return 0;
377 }
378
379 switch(size) {
380 case SIS630_BYTE:
381 case SIS630_BYTE_DATA:
382 data->byte = sis630_read(SMB_BYTE);
383 break;
384 case SIS630_PCALL:
385 case SIS630_WORD_DATA:
386 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
387 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 }
389
390 return 0;
391}
392
393static u32 sis630_func(struct i2c_adapter *adapter)
394{
395 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
396 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
397 I2C_FUNC_SMBUS_BLOCK_DATA;
398}
399
Bill Pemberton0b255e92012-11-27 15:59:38 -0500400static int sis630_setup(struct pci_dev *sis630_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
402 unsigned char b;
403 struct pci_dev *dummy = NULL;
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100404 int retval, i;
Amaury Decrême974d6a32013-01-28 22:21:05 +0100405 /* acpi base address */
406 unsigned short acpi_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 /* check for supported SiS devices */
409 for (i=0; supported[i] > 0 ; i++) {
410 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
411 break; /* found */
412 }
413
414 if (dummy) {
415 pci_dev_put(dummy);
416 }
417 else if (force) {
418 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
419 "loading because of force option enabled\n");
420 }
421 else {
422 return -ENODEV;
423 }
424
425 /*
426 Enable ACPI first , so we can accsess reg 74-75
427 in acpi io space and read acpi base addr
428 */
429 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
430 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100431 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 goto exit;
433 }
434 /* if ACPI already enabled , do nothing */
435 if (!(b & 0x80) &&
436 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
437 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100438 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 goto exit;
440 }
441
442 /* Determine the ACPI base address */
443 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
444 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100445 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 goto exit;
447 }
448
449 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
450
Amaury Decrême974d6a32013-01-28 22:21:05 +0100451 if (supported[i] == PCI_DEVICE_ID_SI_760)
452 smbus_base = acpi_base + 0xE0;
453 else
454 smbus_base = acpi_base + 0x80;
455
456 dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base);
457
458 retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvare54fb4a052008-07-14 22:38:33 +0200459 sis630_driver.name);
460 if (retval)
461 goto exit;
462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 /* Everything is happy, let's grab the memory and set things up. */
Amaury Decrême974d6a32013-01-28 22:21:05 +0100464 if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvared6072f82005-09-25 16:37:04 +0200465 sis630_driver.name)) {
Amaury Decrême974d6a32013-01-28 22:21:05 +0100466 dev_err(&sis630_dev->dev,
467 "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
468 smbus_base + SMB_STS,
469 smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100470 retval = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 goto exit;
472 }
473
474 retval = 0;
475
476exit:
477 if (retval)
Amaury Decrême974d6a32013-01-28 22:21:05 +0100478 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 return retval;
480}
481
482
Jean Delvare8f9082c2006-09-03 22:39:46 +0200483static const struct i2c_algorithm smbus_algorithm = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 .smbus_xfer = sis630_access,
485 .functionality = sis630_func,
486};
487
488static struct i2c_adapter sis630_adapter = {
489 .owner = THIS_MODULE,
Jean Delvare3401b2f2008-07-14 22:38:29 +0200490 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 .algo = &smbus_algorithm,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100492 .retries = 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493};
494
Axel Lin3527bd52012-01-12 20:32:04 +0100495static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
497 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
Amaury Decrême974d6a32013-01-28 22:21:05 +0100498 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 { 0, }
500};
501
502MODULE_DEVICE_TABLE (pci, sis630_ids);
503
Bill Pemberton0b255e92012-11-27 15:59:38 -0500504static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505{
506 if (sis630_setup(dev)) {
507 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
508 return -ENODEV;
509 }
510
Robert P. J. Day405ae7d2007-02-17 19:13:42 +0100511 /* set up the sysfs linkage to our parent device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 sis630_adapter.dev.parent = &dev->dev;
513
Jean Delvare66c7acf2009-01-07 14:29:18 +0100514 snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
Amaury Decrême974d6a32013-01-28 22:21:05 +0100515 "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
517 return i2c_add_adapter(&sis630_adapter);
518}
519
Bill Pemberton0b255e92012-11-27 15:59:38 -0500520static void sis630_remove(struct pci_dev *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100522 if (smbus_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 i2c_del_adapter(&sis630_adapter);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100524 release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
525 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
527}
528
529
530static struct pci_driver sis630_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 .name = "sis630_smbus",
532 .id_table = sis630_ids,
533 .probe = sis630_probe,
Bill Pemberton0b255e92012-11-27 15:59:38 -0500534 .remove = sis630_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535};
536
Axel Lin56f21782012-07-24 14:13:56 +0200537module_pci_driver(sis630_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539MODULE_LICENSE("GPL");
540MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
541MODULE_DESCRIPTION("SIS630 SMBus driver");