1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun /*
8*4882a593Smuzhiyun This module must be considered BETA unless and until
9*4882a593Smuzhiyun the chipset manufacturer releases a datasheet.
10*4882a593Smuzhiyun The register definitions are based on the SiS630.
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c)
13*4882a593Smuzhiyun for just about every machine for which users have reported.
14*4882a593Smuzhiyun If this module isn't detecting your 96x south bridge, have a
15*4882a593Smuzhiyun look there.
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun We assume there can only be one SiS96x with one SMBus interface.
18*4882a593Smuzhiyun */
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <linux/module.h>
21*4882a593Smuzhiyun #include <linux/pci.h>
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/delay.h>
24*4882a593Smuzhiyun #include <linux/stddef.h>
25*4882a593Smuzhiyun #include <linux/ioport.h>
26*4882a593Smuzhiyun #include <linux/i2c.h>
27*4882a593Smuzhiyun #include <linux/acpi.h>
28*4882a593Smuzhiyun #include <linux/io.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /* base address register in PCI config space */
31*4882a593Smuzhiyun #define SIS96x_BAR 0x04
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /* SiS96x SMBus registers */
34*4882a593Smuzhiyun #define SMB_STS 0x00
35*4882a593Smuzhiyun #define SMB_EN 0x01
36*4882a593Smuzhiyun #define SMB_CNT 0x02
37*4882a593Smuzhiyun #define SMB_HOST_CNT 0x03
38*4882a593Smuzhiyun #define SMB_ADDR 0x04
39*4882a593Smuzhiyun #define SMB_CMD 0x05
40*4882a593Smuzhiyun #define SMB_PCOUNT 0x06
41*4882a593Smuzhiyun #define SMB_COUNT 0x07
42*4882a593Smuzhiyun #define SMB_BYTE 0x08
43*4882a593Smuzhiyun #define SMB_DEV_ADDR 0x10
44*4882a593Smuzhiyun #define SMB_DB0 0x11
45*4882a593Smuzhiyun #define SMB_DB1 0x12
46*4882a593Smuzhiyun #define SMB_SAA 0x13
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /* register count for request_region */
49*4882a593Smuzhiyun #define SMB_IOSIZE 0x20
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* Other settings */
52*4882a593Smuzhiyun #define MAX_TIMEOUT 500
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* SiS96x SMBus constants */
55*4882a593Smuzhiyun #define SIS96x_QUICK 0x00
56*4882a593Smuzhiyun #define SIS96x_BYTE 0x01
57*4882a593Smuzhiyun #define SIS96x_BYTE_DATA 0x02
58*4882a593Smuzhiyun #define SIS96x_WORD_DATA 0x03
59*4882a593Smuzhiyun #define SIS96x_PROC_CALL 0x04
60*4882a593Smuzhiyun #define SIS96x_BLOCK_DATA 0x05
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun static struct pci_driver sis96x_driver;
63*4882a593Smuzhiyun static struct i2c_adapter sis96x_adapter;
64*4882a593Smuzhiyun static u16 sis96x_smbus_base;
65*4882a593Smuzhiyun
sis96x_read(u8 reg)66*4882a593Smuzhiyun static inline u8 sis96x_read(u8 reg)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun return inb(sis96x_smbus_base + reg) ;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
sis96x_write(u8 reg,u8 data)71*4882a593Smuzhiyun static inline void sis96x_write(u8 reg, u8 data)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun outb(data, sis96x_smbus_base + reg) ;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun /* Execute a SMBus transaction.
77*4882a593Smuzhiyun int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA
78*4882a593Smuzhiyun */
sis96x_transaction(int size)79*4882a593Smuzhiyun static int sis96x_transaction(int size)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun int temp;
82*4882a593Smuzhiyun int result = 0;
83*4882a593Smuzhiyun int timeout = 0;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* Make sure the SMBus host is ready to start transmitting */
88*4882a593Smuzhiyun if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). "
91*4882a593Smuzhiyun "Resetting...\n", temp);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun /* kill the transaction */
94*4882a593Smuzhiyun sis96x_write(SMB_HOST_CNT, 0x20);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun /* check it again */
97*4882a593Smuzhiyun if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
98*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp);
99*4882a593Smuzhiyun return -EBUSY;
100*4882a593Smuzhiyun } else {
101*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "Successful\n");
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /* Turn off timeout interrupts, set fast host clock */
106*4882a593Smuzhiyun sis96x_write(SMB_CNT, 0x20);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /* clear all (sticky) status flags */
109*4882a593Smuzhiyun temp = sis96x_read(SMB_STS);
110*4882a593Smuzhiyun sis96x_write(SMB_STS, temp & 0x1e);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun /* start the transaction by setting bit 4 and size bits */
113*4882a593Smuzhiyun sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun /* We will always wait for a fraction of a second! */
116*4882a593Smuzhiyun do {
117*4882a593Smuzhiyun msleep(1);
118*4882a593Smuzhiyun temp = sis96x_read(SMB_STS);
119*4882a593Smuzhiyun } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun /* If the SMBus is still busy, we give up */
122*4882a593Smuzhiyun if (timeout > MAX_TIMEOUT) {
123*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp);
124*4882a593Smuzhiyun result = -ETIMEDOUT;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* device error - probably missing ACK */
128*4882a593Smuzhiyun if (temp & 0x02) {
129*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n");
130*4882a593Smuzhiyun result = -ENXIO;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /* bus collision */
134*4882a593Smuzhiyun if (temp & 0x04) {
135*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "Bus collision!\n");
136*4882a593Smuzhiyun result = -EIO;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun /* Finish up by resetting the bus */
140*4882a593Smuzhiyun sis96x_write(SMB_STS, temp);
141*4882a593Smuzhiyun if ((temp = sis96x_read(SMB_STS))) {
142*4882a593Smuzhiyun dev_dbg(&sis96x_adapter.dev, "Failed reset at "
143*4882a593Smuzhiyun "end of transaction! (0x%02x)\n", temp);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun return result;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun /* Return negative errno on error. */
sis96x_access(struct i2c_adapter * adap,u16 addr,unsigned short flags,char read_write,u8 command,int size,union i2c_smbus_data * data)150*4882a593Smuzhiyun static s32 sis96x_access(struct i2c_adapter * adap, u16 addr,
151*4882a593Smuzhiyun unsigned short flags, char read_write,
152*4882a593Smuzhiyun u8 command, int size, union i2c_smbus_data * data)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun int status;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun switch (size) {
157*4882a593Smuzhiyun case I2C_SMBUS_QUICK:
158*4882a593Smuzhiyun sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
159*4882a593Smuzhiyun size = SIS96x_QUICK;
160*4882a593Smuzhiyun break;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun case I2C_SMBUS_BYTE:
163*4882a593Smuzhiyun sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
164*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE)
165*4882a593Smuzhiyun sis96x_write(SMB_CMD, command);
166*4882a593Smuzhiyun size = SIS96x_BYTE;
167*4882a593Smuzhiyun break;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun case I2C_SMBUS_BYTE_DATA:
170*4882a593Smuzhiyun sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
171*4882a593Smuzhiyun sis96x_write(SMB_CMD, command);
172*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE)
173*4882a593Smuzhiyun sis96x_write(SMB_BYTE, data->byte);
174*4882a593Smuzhiyun size = SIS96x_BYTE_DATA;
175*4882a593Smuzhiyun break;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun case I2C_SMBUS_PROC_CALL:
178*4882a593Smuzhiyun case I2C_SMBUS_WORD_DATA:
179*4882a593Smuzhiyun sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
180*4882a593Smuzhiyun sis96x_write(SMB_CMD, command);
181*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE) {
182*4882a593Smuzhiyun sis96x_write(SMB_BYTE, data->word & 0xff);
183*4882a593Smuzhiyun sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun size = (size == I2C_SMBUS_PROC_CALL ?
186*4882a593Smuzhiyun SIS96x_PROC_CALL : SIS96x_WORD_DATA);
187*4882a593Smuzhiyun break;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun default:
190*4882a593Smuzhiyun dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
191*4882a593Smuzhiyun return -EOPNOTSUPP;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun status = sis96x_transaction(size);
195*4882a593Smuzhiyun if (status)
196*4882a593Smuzhiyun return status;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun if ((size != SIS96x_PROC_CALL) &&
199*4882a593Smuzhiyun ((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK)))
200*4882a593Smuzhiyun return 0;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun switch (size) {
203*4882a593Smuzhiyun case SIS96x_BYTE:
204*4882a593Smuzhiyun case SIS96x_BYTE_DATA:
205*4882a593Smuzhiyun data->byte = sis96x_read(SMB_BYTE);
206*4882a593Smuzhiyun break;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun case SIS96x_WORD_DATA:
209*4882a593Smuzhiyun case SIS96x_PROC_CALL:
210*4882a593Smuzhiyun data->word = sis96x_read(SMB_BYTE) +
211*4882a593Smuzhiyun (sis96x_read(SMB_BYTE + 1) << 8);
212*4882a593Smuzhiyun break;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun return 0;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
sis96x_func(struct i2c_adapter * adapter)217*4882a593Smuzhiyun static u32 sis96x_func(struct i2c_adapter *adapter)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
220*4882a593Smuzhiyun I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
221*4882a593Smuzhiyun I2C_FUNC_SMBUS_PROC_CALL;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun static const struct i2c_algorithm smbus_algorithm = {
225*4882a593Smuzhiyun .smbus_xfer = sis96x_access,
226*4882a593Smuzhiyun .functionality = sis96x_func,
227*4882a593Smuzhiyun };
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun static struct i2c_adapter sis96x_adapter = {
230*4882a593Smuzhiyun .owner = THIS_MODULE,
231*4882a593Smuzhiyun .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
232*4882a593Smuzhiyun .algo = &smbus_algorithm,
233*4882a593Smuzhiyun };
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun static const struct pci_device_id sis96x_ids[] = {
236*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) },
237*4882a593Smuzhiyun { 0, }
238*4882a593Smuzhiyun };
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun MODULE_DEVICE_TABLE (pci, sis96x_ids);
241*4882a593Smuzhiyun
sis96x_probe(struct pci_dev * dev,const struct pci_device_id * id)242*4882a593Smuzhiyun static int sis96x_probe(struct pci_dev *dev,
243*4882a593Smuzhiyun const struct pci_device_id *id)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun u16 ww = 0;
246*4882a593Smuzhiyun int retval;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun if (sis96x_smbus_base) {
249*4882a593Smuzhiyun dev_err(&dev->dev, "Only one device supported.\n");
250*4882a593Smuzhiyun return -EBUSY;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww);
254*4882a593Smuzhiyun if (PCI_CLASS_SERIAL_SMBUS != ww) {
255*4882a593Smuzhiyun dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww);
256*4882a593Smuzhiyun return -ENODEV;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR);
260*4882a593Smuzhiyun if (!sis96x_smbus_base) {
261*4882a593Smuzhiyun dev_err(&dev->dev, "SiS96x SMBus base address "
262*4882a593Smuzhiyun "not initialized!\n");
263*4882a593Smuzhiyun return -EINVAL;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n",
266*4882a593Smuzhiyun sis96x_smbus_base);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]);
269*4882a593Smuzhiyun if (retval)
270*4882a593Smuzhiyun return -ENODEV;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun /* Everything is happy, let's grab the memory and set things up. */
273*4882a593Smuzhiyun if (!request_region(sis96x_smbus_base, SMB_IOSIZE,
274*4882a593Smuzhiyun sis96x_driver.name)) {
275*4882a593Smuzhiyun dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x "
276*4882a593Smuzhiyun "already in use!\n", sis96x_smbus_base,
277*4882a593Smuzhiyun sis96x_smbus_base + SMB_IOSIZE - 1);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun sis96x_smbus_base = 0;
280*4882a593Smuzhiyun return -EINVAL;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun /* set up the sysfs linkage to our parent device */
284*4882a593Smuzhiyun sis96x_adapter.dev.parent = &dev->dev;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun snprintf(sis96x_adapter.name, sizeof(sis96x_adapter.name),
287*4882a593Smuzhiyun "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun if ((retval = i2c_add_adapter(&sis96x_adapter))) {
290*4882a593Smuzhiyun dev_err(&dev->dev, "Couldn't register adapter!\n");
291*4882a593Smuzhiyun release_region(sis96x_smbus_base, SMB_IOSIZE);
292*4882a593Smuzhiyun sis96x_smbus_base = 0;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun return retval;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
sis96x_remove(struct pci_dev * dev)298*4882a593Smuzhiyun static void sis96x_remove(struct pci_dev *dev)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun if (sis96x_smbus_base) {
301*4882a593Smuzhiyun i2c_del_adapter(&sis96x_adapter);
302*4882a593Smuzhiyun release_region(sis96x_smbus_base, SMB_IOSIZE);
303*4882a593Smuzhiyun sis96x_smbus_base = 0;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun static struct pci_driver sis96x_driver = {
308*4882a593Smuzhiyun .name = "sis96x_smbus",
309*4882a593Smuzhiyun .id_table = sis96x_ids,
310*4882a593Smuzhiyun .probe = sis96x_probe,
311*4882a593Smuzhiyun .remove = sis96x_remove,
312*4882a593Smuzhiyun };
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun module_pci_driver(sis96x_driver);
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
317*4882a593Smuzhiyun MODULE_DESCRIPTION("SiS96x SMBus driver");
318*4882a593Smuzhiyun MODULE_LICENSE("GPL");
319