xref: /OK3568_Linux_fs/kernel/drivers/i2c/busses/i2c-sis96x.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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