blob: b2fa7415f7e47cf72544500f780418d282b537dd [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
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +010084/* SMB_STS register */
85#define BYTE_DONE_STS 0x10 /* Byte Done Status / Block Array */
86#define SMBCOL_STS 0x04 /* Collision */
87#define SMBERR_STS 0x02 /* Device error */
88
89/* SMB_CNT register */
90#define MSTO_EN 0x40 /* Host Master Timeout Enable */
91#define SMBCLK_SEL 0x20 /* Host master clock selection */
92#define SMB_PROBE 0x02 /* Bus Probe/Slave busy */
93#define SMB_HOSTBUSY 0x01 /* Host Busy */
94
95/* SMBHOST_CNT register */
96#define SMB_KILL 0x20 /* Kill */
97#define SMB_START 0x10 /* Start */
98
Amaury Decrême974d6a32013-01-28 22:21:05 +010099/* register count for request_region
100 * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964
101 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102#define SIS630_SMB_IOREGION 20
103
104/* PCI address constants */
105/* acpi base address register */
106#define SIS630_ACPI_BASE_REG 0x74
107/* bios control register */
108#define SIS630_BIOS_CTL_REG 0x40
109
110/* Other settings */
111#define MAX_TIMEOUT 500
112
113/* SIS630 constants */
114#define SIS630_QUICK 0x00
115#define SIS630_BYTE 0x01
116#define SIS630_BYTE_DATA 0x02
117#define SIS630_WORD_DATA 0x03
118#define SIS630_PCALL 0x04
119#define SIS630_BLOCK_DATA 0x05
120
Jean Delvared6072f82005-09-25 16:37:04 +0200121static struct pci_driver sis630_driver;
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123/* insmod parameters */
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030124static bool high_clock;
125static bool force;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126module_param(high_clock, bool, 0);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100127MODULE_PARM_DESC(high_clock,
128 "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only).");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129module_param(force, bool, 0);
130MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
131
Amaury Decrême974d6a32013-01-28 22:21:05 +0100132/* SMBus base adress */
133static unsigned short smbus_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
135/* supported chips */
136static int supported[] = {
137 PCI_DEVICE_ID_SI_630,
138 PCI_DEVICE_ID_SI_730,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100139 PCI_DEVICE_ID_SI_760,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 0 /* terminates the list */
141};
142
143static inline u8 sis630_read(u8 reg)
144{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100145 return inb(smbus_base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
148static inline void sis630_write(u8 reg, u8 data)
149{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100150 outb(data, smbus_base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151}
152
153static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
154{
155 int temp;
156
157 /* Make sure the SMBus host is ready to start transmitting. */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100158 temp = sis630_read(SMB_CNT);
159 if ((temp & (SMB_PROBE | SMB_HOSTBUSY)) != 0x00) {
160 dev_dbg(&adap->dev, "SMBus busy (%02x). Resetting...\n", temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 /* kill smbus transaction */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100162 sis630_write(SMBHOST_CNT, SMB_KILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100164 temp = sis630_read(SMB_CNT);
165 if (temp & (SMB_PROBE | SMB_HOSTBUSY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
David Brownell97140342008-07-14 22:38:25 +0200167 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 } else {
Jean Delvarec5d21b72008-04-29 23:11:37 +0200169 dev_dbg(&adap->dev, "Successful!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 }
171 }
172
173 /* save old clock, so we can prevent machine for hung */
174 *oldclock = sis630_read(SMB_CNT);
175
176 dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
177
178 /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
179 if (high_clock)
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100180 sis630_write(SMB_CNT, SMBCLK_SEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 else
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100182 sis630_write(SMB_CNT, (*oldclock & ~MSTO_EN));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
184 /* clear all sticky bits */
185 temp = sis630_read(SMB_STS);
186 sis630_write(SMB_STS, temp & 0x1e);
187
188 /* start the transaction by setting bit 4 and size */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100189 sis630_write(SMBHOST_CNT, SMB_START | (size & 0x07));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191 return 0;
192}
193
194static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
195{
196 int temp, result = 0, timeout = 0;
197
198 /* We will always wait for a fraction of a second! */
199 do {
200 msleep(1);
201 temp = sis630_read(SMB_STS);
202 /* check if block transmitted */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100203 if (size == SIS630_BLOCK_DATA && (temp & BYTE_DONE_STS))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 break;
205 } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
206
207 /* If the SMBus is still busy, we give up */
Roel Kluin4ccc28f2009-05-05 08:39:24 +0200208 if (timeout > MAX_TIMEOUT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 dev_dbg(&adap->dev, "SMBus Timeout!\n");
David Brownell97140342008-07-14 22:38:25 +0200210 result = -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 }
212
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100213 if (temp & SMBERR_STS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
David Brownell97140342008-07-14 22:38:25 +0200215 result = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 }
217
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100218 if (temp & SMBCOL_STS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 dev_err(&adap->dev, "Bus collision!\n");
Amaury Decrême499b9192013-01-28 22:21:07 +0100220 result = -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 }
222
223 return result;
224}
225
226static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
227{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 /* clear all status "sticky" bits */
Amaury Decrêmeaa9e7a32013-01-28 22:21:06 +0100229 sis630_write(SMB_STS, 0xFF);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
231 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
232
233 /*
234 * restore old Host Master Clock if high_clock is set
235 * and oldclock was not 56KHz
236 */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100237 if (high_clock && !(oldclock & SMBCLK_SEL))
238 sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
241}
242
243static int sis630_transaction(struct i2c_adapter *adap, int size)
244{
245 int result = 0;
246 u8 oldclock = 0;
247
248 result = sis630_transaction_start(adap, size, &oldclock);
249 if (!result) {
250 result = sis630_transaction_wait(adap, size);
251 sis630_transaction_end(adap, oldclock);
252 }
253
254 return result;
255}
256
257static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
258{
259 int i, len = 0, rc = 0;
260 u8 oldclock = 0;
261
262 if (read_write == I2C_SMBUS_WRITE) {
263 len = data->block[0];
264 if (len < 0)
265 len = 0;
266 else if (len > 32)
267 len = 32;
268 sis630_write(SMB_COUNT, len);
269 for (i=1; i <= len; i++) {
270 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
271 /* set data */
272 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
273 if (i==8 || (len<8 && i==len)) {
274 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
275 /* first transaction */
David Brownell97140342008-07-14 22:38:25 +0200276 rc = sis630_transaction_start(adap,
277 SIS630_BLOCK_DATA, &oldclock);
278 if (rc)
279 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 }
281 else if ((i-1)%8 == 7 || i==len) {
282 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
283 if (i>8) {
284 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
285 /*
286 If this is not first transaction,
287 we must clear sticky bit.
288 clear SMBARY_STS
289 */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100290 sis630_write(SMB_STS, BYTE_DONE_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
David Brownell97140342008-07-14 22:38:25 +0200292 rc = sis630_transaction_wait(adap,
293 SIS630_BLOCK_DATA);
294 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 break;
297 }
298 }
299 }
300 }
301 else {
302 /* read request */
303 data->block[0] = len = 0;
David Brownell97140342008-07-14 22:38:25 +0200304 rc = sis630_transaction_start(adap,
305 SIS630_BLOCK_DATA, &oldclock);
306 if (rc)
307 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 do {
David Brownell97140342008-07-14 22:38:25 +0200309 rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
310 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 dev_dbg(&adap->dev, "trans_wait failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 break;
313 }
314 /* if this first transaction then read byte count */
315 if (len == 0)
316 data->block[0] = sis630_read(SMB_COUNT);
317
318 /* just to be sure */
319 if (data->block[0] > 32)
320 data->block[0] = 32;
321
322 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
323
324 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
325 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
326 data->block[len+1] = sis630_read(SMB_BYTE+i);
327 }
328
329 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
330
331 /* clear SMBARY_STS */
Amaury Decrêmea6e7d0ef2013-01-28 22:21:08 +0100332 sis630_write(SMB_STS, BYTE_DONE_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 } while(len < data->block[0]);
334 }
335
336 sis630_transaction_end(adap, oldclock);
337
338 return rc;
339}
340
David Brownell97140342008-07-14 22:38:25 +0200341/* Return negative errno on error. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
343 unsigned short flags, char read_write,
344 u8 command, int size, union i2c_smbus_data *data)
345{
David Brownell97140342008-07-14 22:38:25 +0200346 int status;
347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 switch (size) {
349 case I2C_SMBUS_QUICK:
350 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
351 size = SIS630_QUICK;
352 break;
353 case I2C_SMBUS_BYTE:
354 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
355 if (read_write == I2C_SMBUS_WRITE)
356 sis630_write(SMB_CMD, command);
357 size = SIS630_BYTE;
358 break;
359 case I2C_SMBUS_BYTE_DATA:
360 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
361 sis630_write(SMB_CMD, command);
362 if (read_write == I2C_SMBUS_WRITE)
363 sis630_write(SMB_BYTE, data->byte);
364 size = SIS630_BYTE_DATA;
365 break;
366 case I2C_SMBUS_PROC_CALL:
367 case I2C_SMBUS_WORD_DATA:
368 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
369 sis630_write(SMB_CMD, command);
370 if (read_write == I2C_SMBUS_WRITE) {
371 sis630_write(SMB_BYTE, data->word & 0xff);
372 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
373 }
374 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
375 break;
376 case I2C_SMBUS_BLOCK_DATA:
377 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
378 sis630_write(SMB_CMD, command);
379 size = SIS630_BLOCK_DATA;
380 return sis630_block_data(adap, data, read_write);
381 default:
Jean Delvareac7fc4f2008-07-14 22:38:25 +0200382 dev_warn(&adap->dev, "Unsupported transaction %d\n",
383 size);
David Brownell97140342008-07-14 22:38:25 +0200384 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 }
386
David Brownell97140342008-07-14 22:38:25 +0200387 status = sis630_transaction(adap, size);
388 if (status)
389 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391 if ((size != SIS630_PCALL) &&
392 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
393 return 0;
394 }
395
396 switch(size) {
397 case SIS630_BYTE:
398 case SIS630_BYTE_DATA:
399 data->byte = sis630_read(SMB_BYTE);
400 break;
401 case SIS630_PCALL:
402 case SIS630_WORD_DATA:
403 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
404 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 }
406
407 return 0;
408}
409
410static u32 sis630_func(struct i2c_adapter *adapter)
411{
412 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
413 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
414 I2C_FUNC_SMBUS_BLOCK_DATA;
415}
416
Bill Pemberton0b255e92012-11-27 15:59:38 -0500417static int sis630_setup(struct pci_dev *sis630_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
419 unsigned char b;
420 struct pci_dev *dummy = NULL;
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100421 int retval, i;
Amaury Decrême974d6a32013-01-28 22:21:05 +0100422 /* acpi base address */
423 unsigned short acpi_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 /* check for supported SiS devices */
426 for (i=0; supported[i] > 0 ; i++) {
427 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
428 break; /* found */
429 }
430
431 if (dummy) {
432 pci_dev_put(dummy);
433 }
434 else if (force) {
435 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
436 "loading because of force option enabled\n");
437 }
438 else {
439 return -ENODEV;
440 }
441
442 /*
443 Enable ACPI first , so we can accsess reg 74-75
444 in acpi io space and read acpi base addr
445 */
446 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
447 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100448 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 goto exit;
450 }
451 /* if ACPI already enabled , do nothing */
452 if (!(b & 0x80) &&
453 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
454 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100455 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 goto exit;
457 }
458
459 /* Determine the ACPI base address */
460 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
461 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100462 retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 goto exit;
464 }
465
466 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
467
Amaury Decrême974d6a32013-01-28 22:21:05 +0100468 if (supported[i] == PCI_DEVICE_ID_SI_760)
469 smbus_base = acpi_base + 0xE0;
470 else
471 smbus_base = acpi_base + 0x80;
472
473 dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base);
474
475 retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvare54fb4a052008-07-14 22:38:33 +0200476 sis630_driver.name);
477 if (retval)
478 goto exit;
479
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 /* Everything is happy, let's grab the memory and set things up. */
Amaury Decrême974d6a32013-01-28 22:21:05 +0100481 if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
Jean Delvared6072f82005-09-25 16:37:04 +0200482 sis630_driver.name)) {
Amaury Decrême974d6a32013-01-28 22:21:05 +0100483 dev_err(&sis630_dev->dev,
484 "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
485 smbus_base + SMB_STS,
486 smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
Jean Delvare7c1f59c2012-01-12 20:32:03 +0100487 retval = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 goto exit;
489 }
490
491 retval = 0;
492
493exit:
494 if (retval)
Amaury Decrême974d6a32013-01-28 22:21:05 +0100495 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 return retval;
497}
498
499
Jean Delvare8f9082c2006-09-03 22:39:46 +0200500static const struct i2c_algorithm smbus_algorithm = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 .smbus_xfer = sis630_access,
502 .functionality = sis630_func,
503};
504
505static struct i2c_adapter sis630_adapter = {
506 .owner = THIS_MODULE,
Jean Delvare3401b2f2008-07-14 22:38:29 +0200507 .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 .algo = &smbus_algorithm,
Amaury Decrême974d6a32013-01-28 22:21:05 +0100509 .retries = 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510};
511
Axel Lin3527bd52012-01-12 20:32:04 +0100512static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
514 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
Amaury Decrême974d6a32013-01-28 22:21:05 +0100515 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 { 0, }
517};
518
519MODULE_DEVICE_TABLE (pci, sis630_ids);
520
Bill Pemberton0b255e92012-11-27 15:59:38 -0500521static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
523 if (sis630_setup(dev)) {
524 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
525 return -ENODEV;
526 }
527
Robert P. J. Day405ae7d2007-02-17 19:13:42 +0100528 /* set up the sysfs linkage to our parent device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 sis630_adapter.dev.parent = &dev->dev;
530
Jean Delvare66c7acf2009-01-07 14:29:18 +0100531 snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
Amaury Decrême974d6a32013-01-28 22:21:05 +0100532 "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 return i2c_add_adapter(&sis630_adapter);
535}
536
Bill Pemberton0b255e92012-11-27 15:59:38 -0500537static void sis630_remove(struct pci_dev *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Amaury Decrême974d6a32013-01-28 22:21:05 +0100539 if (smbus_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 i2c_del_adapter(&sis630_adapter);
Amaury Decrême974d6a32013-01-28 22:21:05 +0100541 release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
542 smbus_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
544}
545
546
547static struct pci_driver sis630_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 .name = "sis630_smbus",
549 .id_table = sis630_ids,
550 .probe = sis630_probe,
Bill Pemberton0b255e92012-11-27 15:59:38 -0500551 .remove = sis630_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552};
553
Axel Lin56f21782012-07-24 14:13:56 +0200554module_pci_driver(sis630_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556MODULE_LICENSE("GPL");
557MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
558MODULE_DESCRIPTION("SIS630 SMBus driver");