1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun Shamelessly ripped from i2c-piix4.c:
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
8*4882a593Smuzhiyun Philip Edelbrock <phil@netroedge.com>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun /*
13*4882a593Smuzhiyun 2002-04-08: Added nForce support. (Csaba Halasz)
14*4882a593Smuzhiyun 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
15*4882a593Smuzhiyun 2002-12-28: Rewritten into something that resembles a Linux driver (hch)
16*4882a593Smuzhiyun 2003-11-29: Added back AMD8111 removed by the previous rewrite.
17*4882a593Smuzhiyun (Philip Pokorny)
18*4882a593Smuzhiyun */
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce
22*4882a593Smuzhiyun Note: we assume there can only be one device, with one SMBus interface.
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <linux/module.h>
26*4882a593Smuzhiyun #include <linux/pci.h>
27*4882a593Smuzhiyun #include <linux/kernel.h>
28*4882a593Smuzhiyun #include <linux/delay.h>
29*4882a593Smuzhiyun #include <linux/stddef.h>
30*4882a593Smuzhiyun #include <linux/ioport.h>
31*4882a593Smuzhiyun #include <linux/i2c.h>
32*4882a593Smuzhiyun #include <linux/acpi.h>
33*4882a593Smuzhiyun #include <linux/io.h>
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /* AMD756 SMBus address offsets */
36*4882a593Smuzhiyun #define SMB_ADDR_OFFSET 0xE0
37*4882a593Smuzhiyun #define SMB_IOSIZE 16
38*4882a593Smuzhiyun #define SMB_GLOBAL_STATUS (0x0 + amd756_ioport)
39*4882a593Smuzhiyun #define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport)
40*4882a593Smuzhiyun #define SMB_HOST_ADDRESS (0x4 + amd756_ioport)
41*4882a593Smuzhiyun #define SMB_HOST_DATA (0x6 + amd756_ioport)
42*4882a593Smuzhiyun #define SMB_HOST_COMMAND (0x8 + amd756_ioport)
43*4882a593Smuzhiyun #define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport)
44*4882a593Smuzhiyun #define SMB_HAS_DATA (0xA + amd756_ioport)
45*4882a593Smuzhiyun #define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport)
46*4882a593Smuzhiyun #define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport)
47*4882a593Smuzhiyun #define SMB_SNOOP_ADDRESS (0xF + amd756_ioport)
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* PCI Address Constants */
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* address of I/O space */
52*4882a593Smuzhiyun #define SMBBA 0x058 /* mh */
53*4882a593Smuzhiyun #define SMBBANFORCE 0x014
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun /* general configuration */
56*4882a593Smuzhiyun #define SMBGCFG 0x041 /* mh */
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* silicon revision code */
59*4882a593Smuzhiyun #define SMBREV 0x008
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun /* Other settings */
62*4882a593Smuzhiyun #define MAX_TIMEOUT 500
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* AMD756 constants */
65*4882a593Smuzhiyun #define AMD756_QUICK 0x00
66*4882a593Smuzhiyun #define AMD756_BYTE 0x01
67*4882a593Smuzhiyun #define AMD756_BYTE_DATA 0x02
68*4882a593Smuzhiyun #define AMD756_WORD_DATA 0x03
69*4882a593Smuzhiyun #define AMD756_PROCESS_CALL 0x04
70*4882a593Smuzhiyun #define AMD756_BLOCK_DATA 0x05
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun static struct pci_driver amd756_driver;
73*4882a593Smuzhiyun static unsigned short amd756_ioport;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun SMBUS event = I/O 28-29 bit 11
77*4882a593Smuzhiyun see E0 for the status bits and enabled in E2
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun */
80*4882a593Smuzhiyun #define GS_ABRT_STS (1 << 0)
81*4882a593Smuzhiyun #define GS_COL_STS (1 << 1)
82*4882a593Smuzhiyun #define GS_PRERR_STS (1 << 2)
83*4882a593Smuzhiyun #define GS_HST_STS (1 << 3)
84*4882a593Smuzhiyun #define GS_HCYC_STS (1 << 4)
85*4882a593Smuzhiyun #define GS_TO_STS (1 << 5)
86*4882a593Smuzhiyun #define GS_SMB_STS (1 << 11)
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun #define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
89*4882a593Smuzhiyun GS_HCYC_STS | GS_TO_STS )
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun #define GE_CYC_TYPE_MASK (7)
92*4882a593Smuzhiyun #define GE_HOST_STC (1 << 3)
93*4882a593Smuzhiyun #define GE_ABORT (1 << 5)
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun
amd756_transaction(struct i2c_adapter * adap)96*4882a593Smuzhiyun static int amd756_transaction(struct i2c_adapter *adap)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun int temp;
99*4882a593Smuzhiyun int result = 0;
100*4882a593Smuzhiyun int timeout = 0;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, "
103*4882a593Smuzhiyun "DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS),
104*4882a593Smuzhiyun inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS),
105*4882a593Smuzhiyun inb_p(SMB_HOST_DATA));
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun /* Make sure the SMBus host is ready to start transmitting */
108*4882a593Smuzhiyun if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
109*4882a593Smuzhiyun dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp);
110*4882a593Smuzhiyun do {
111*4882a593Smuzhiyun msleep(1);
112*4882a593Smuzhiyun temp = inw_p(SMB_GLOBAL_STATUS);
113*4882a593Smuzhiyun } while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
114*4882a593Smuzhiyun (timeout++ < MAX_TIMEOUT));
115*4882a593Smuzhiyun /* If the SMBus is still busy, we give up */
116*4882a593Smuzhiyun if (timeout > MAX_TIMEOUT) {
117*4882a593Smuzhiyun dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp);
118*4882a593Smuzhiyun goto abort;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun timeout = 0;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* start the transaction by setting the start bit */
124*4882a593Smuzhiyun outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* We will always wait for a fraction of a second! */
127*4882a593Smuzhiyun do {
128*4882a593Smuzhiyun msleep(1);
129*4882a593Smuzhiyun temp = inw_p(SMB_GLOBAL_STATUS);
130*4882a593Smuzhiyun } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun /* If the SMBus is still busy, we give up */
133*4882a593Smuzhiyun if (timeout > MAX_TIMEOUT) {
134*4882a593Smuzhiyun dev_dbg(&adap->dev, "Completion timeout!\n");
135*4882a593Smuzhiyun goto abort;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (temp & GS_PRERR_STS) {
139*4882a593Smuzhiyun result = -ENXIO;
140*4882a593Smuzhiyun dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n");
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (temp & GS_COL_STS) {
144*4882a593Smuzhiyun result = -EIO;
145*4882a593Smuzhiyun dev_warn(&adap->dev, "SMBus collision!\n");
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (temp & GS_TO_STS) {
149*4882a593Smuzhiyun result = -ETIMEDOUT;
150*4882a593Smuzhiyun dev_dbg(&adap->dev, "SMBus protocol timeout!\n");
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (temp & GS_HCYC_STS)
154*4882a593Smuzhiyun dev_dbg(&adap->dev, "SMBus protocol success!\n");
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun #ifdef DEBUG
159*4882a593Smuzhiyun if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
160*4882a593Smuzhiyun dev_dbg(&adap->dev,
161*4882a593Smuzhiyun "Failed reset at end of transaction (%04x)\n", temp);
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun #endif
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun dev_dbg(&adap->dev,
166*4882a593Smuzhiyun "Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
167*4882a593Smuzhiyun inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
168*4882a593Smuzhiyun inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun return result;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun abort:
173*4882a593Smuzhiyun dev_warn(&adap->dev, "Sending abort\n");
174*4882a593Smuzhiyun outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
175*4882a593Smuzhiyun msleep(100);
176*4882a593Smuzhiyun outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
177*4882a593Smuzhiyun return -EIO;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun /* Return negative errno on error. */
amd756_access(struct i2c_adapter * adap,u16 addr,unsigned short flags,char read_write,u8 command,int size,union i2c_smbus_data * data)181*4882a593Smuzhiyun static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
182*4882a593Smuzhiyun unsigned short flags, char read_write,
183*4882a593Smuzhiyun u8 command, int size, union i2c_smbus_data * data)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun int i, len;
186*4882a593Smuzhiyun int status;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun switch (size) {
189*4882a593Smuzhiyun case I2C_SMBUS_QUICK:
190*4882a593Smuzhiyun outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
191*4882a593Smuzhiyun SMB_HOST_ADDRESS);
192*4882a593Smuzhiyun size = AMD756_QUICK;
193*4882a593Smuzhiyun break;
194*4882a593Smuzhiyun case I2C_SMBUS_BYTE:
195*4882a593Smuzhiyun outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
196*4882a593Smuzhiyun SMB_HOST_ADDRESS);
197*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE)
198*4882a593Smuzhiyun outb_p(command, SMB_HOST_DATA);
199*4882a593Smuzhiyun size = AMD756_BYTE;
200*4882a593Smuzhiyun break;
201*4882a593Smuzhiyun case I2C_SMBUS_BYTE_DATA:
202*4882a593Smuzhiyun outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
203*4882a593Smuzhiyun SMB_HOST_ADDRESS);
204*4882a593Smuzhiyun outb_p(command, SMB_HOST_COMMAND);
205*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE)
206*4882a593Smuzhiyun outw_p(data->byte, SMB_HOST_DATA);
207*4882a593Smuzhiyun size = AMD756_BYTE_DATA;
208*4882a593Smuzhiyun break;
209*4882a593Smuzhiyun case I2C_SMBUS_WORD_DATA:
210*4882a593Smuzhiyun outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
211*4882a593Smuzhiyun SMB_HOST_ADDRESS);
212*4882a593Smuzhiyun outb_p(command, SMB_HOST_COMMAND);
213*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE)
214*4882a593Smuzhiyun outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */
215*4882a593Smuzhiyun size = AMD756_WORD_DATA;
216*4882a593Smuzhiyun break;
217*4882a593Smuzhiyun case I2C_SMBUS_BLOCK_DATA:
218*4882a593Smuzhiyun outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
219*4882a593Smuzhiyun SMB_HOST_ADDRESS);
220*4882a593Smuzhiyun outb_p(command, SMB_HOST_COMMAND);
221*4882a593Smuzhiyun if (read_write == I2C_SMBUS_WRITE) {
222*4882a593Smuzhiyun len = data->block[0];
223*4882a593Smuzhiyun if (len < 0)
224*4882a593Smuzhiyun len = 0;
225*4882a593Smuzhiyun if (len > 32)
226*4882a593Smuzhiyun len = 32;
227*4882a593Smuzhiyun outw_p(len, SMB_HOST_DATA);
228*4882a593Smuzhiyun /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
229*4882a593Smuzhiyun for (i = 1; i <= len; i++)
230*4882a593Smuzhiyun outb_p(data->block[i],
231*4882a593Smuzhiyun SMB_HOST_BLOCK_DATA);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun size = AMD756_BLOCK_DATA;
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun default:
236*4882a593Smuzhiyun dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
237*4882a593Smuzhiyun return -EOPNOTSUPP;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun /* How about enabling interrupts... */
241*4882a593Smuzhiyun outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun status = amd756_transaction(adap);
244*4882a593Smuzhiyun if (status)
245*4882a593Smuzhiyun return status;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
248*4882a593Smuzhiyun return 0;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun switch (size) {
252*4882a593Smuzhiyun case AMD756_BYTE:
253*4882a593Smuzhiyun data->byte = inw_p(SMB_HOST_DATA);
254*4882a593Smuzhiyun break;
255*4882a593Smuzhiyun case AMD756_BYTE_DATA:
256*4882a593Smuzhiyun data->byte = inw_p(SMB_HOST_DATA);
257*4882a593Smuzhiyun break;
258*4882a593Smuzhiyun case AMD756_WORD_DATA:
259*4882a593Smuzhiyun data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */
260*4882a593Smuzhiyun break;
261*4882a593Smuzhiyun case AMD756_BLOCK_DATA:
262*4882a593Smuzhiyun data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
263*4882a593Smuzhiyun if(data->block[0] > 32)
264*4882a593Smuzhiyun data->block[0] = 32;
265*4882a593Smuzhiyun /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
266*4882a593Smuzhiyun for (i = 1; i <= data->block[0]; i++)
267*4882a593Smuzhiyun data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
268*4882a593Smuzhiyun break;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun return 0;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
amd756_func(struct i2c_adapter * adapter)274*4882a593Smuzhiyun static u32 amd756_func(struct i2c_adapter *adapter)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
277*4882a593Smuzhiyun I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
278*4882a593Smuzhiyun I2C_FUNC_SMBUS_BLOCK_DATA;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun static const struct i2c_algorithm smbus_algorithm = {
282*4882a593Smuzhiyun .smbus_xfer = amd756_access,
283*4882a593Smuzhiyun .functionality = amd756_func,
284*4882a593Smuzhiyun };
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun struct i2c_adapter amd756_smbus = {
287*4882a593Smuzhiyun .owner = THIS_MODULE,
288*4882a593Smuzhiyun .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
289*4882a593Smuzhiyun .algo = &smbus_algorithm,
290*4882a593Smuzhiyun };
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
293*4882a593Smuzhiyun static const char* chipname[] = {
294*4882a593Smuzhiyun "AMD756", "AMD766", "AMD768",
295*4882a593Smuzhiyun "nVidia nForce", "AMD8111",
296*4882a593Smuzhiyun };
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun static const struct pci_device_id amd756_ids[] = {
299*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B),
300*4882a593Smuzhiyun .driver_data = AMD756 },
301*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413),
302*4882a593Smuzhiyun .driver_data = AMD766 },
303*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443),
304*4882a593Smuzhiyun .driver_data = AMD768 },
305*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS),
306*4882a593Smuzhiyun .driver_data = AMD8111 },
307*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS),
308*4882a593Smuzhiyun .driver_data = NFORCE },
309*4882a593Smuzhiyun { 0, }
310*4882a593Smuzhiyun };
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun MODULE_DEVICE_TABLE (pci, amd756_ids);
313*4882a593Smuzhiyun
amd756_probe(struct pci_dev * pdev,const struct pci_device_id * id)314*4882a593Smuzhiyun static int amd756_probe(struct pci_dev *pdev, const struct pci_device_id *id)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun int nforce = (id->driver_data == NFORCE);
317*4882a593Smuzhiyun int error;
318*4882a593Smuzhiyun u8 temp;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun if (amd756_ioport) {
321*4882a593Smuzhiyun dev_err(&pdev->dev, "Only one device supported "
322*4882a593Smuzhiyun "(you have a strange motherboard, btw)\n");
323*4882a593Smuzhiyun return -ENODEV;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun if (nforce) {
327*4882a593Smuzhiyun if (PCI_FUNC(pdev->devfn) != 1)
328*4882a593Smuzhiyun return -ENODEV;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport);
331*4882a593Smuzhiyun amd756_ioport &= 0xfffc;
332*4882a593Smuzhiyun } else { /* amd */
333*4882a593Smuzhiyun if (PCI_FUNC(pdev->devfn) != 3)
334*4882a593Smuzhiyun return -ENODEV;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun pci_read_config_byte(pdev, SMBGCFG, &temp);
337*4882a593Smuzhiyun if ((temp & 128) == 0) {
338*4882a593Smuzhiyun dev_err(&pdev->dev,
339*4882a593Smuzhiyun "Error: SMBus controller I/O not enabled!\n");
340*4882a593Smuzhiyun return -ENODEV;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun /* Determine the address of the SMBus areas */
344*4882a593Smuzhiyun /* Technically it is a dword but... */
345*4882a593Smuzhiyun pci_read_config_word(pdev, SMBBA, &amd756_ioport);
346*4882a593Smuzhiyun amd756_ioport &= 0xff00;
347*4882a593Smuzhiyun amd756_ioport += SMB_ADDR_OFFSET;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun error = acpi_check_region(amd756_ioport, SMB_IOSIZE,
351*4882a593Smuzhiyun amd756_driver.name);
352*4882a593Smuzhiyun if (error)
353*4882a593Smuzhiyun return -ENODEV;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) {
356*4882a593Smuzhiyun dev_err(&pdev->dev, "SMB region 0x%x already in use!\n",
357*4882a593Smuzhiyun amd756_ioport);
358*4882a593Smuzhiyun return -ENODEV;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun pci_read_config_byte(pdev, SMBREV, &temp);
362*4882a593Smuzhiyun dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp);
363*4882a593Smuzhiyun dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /* set up the sysfs linkage to our parent device */
366*4882a593Smuzhiyun amd756_smbus.dev.parent = &pdev->dev;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun snprintf(amd756_smbus.name, sizeof(amd756_smbus.name),
369*4882a593Smuzhiyun "SMBus %s adapter at %04x", chipname[id->driver_data],
370*4882a593Smuzhiyun amd756_ioport);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun error = i2c_add_adapter(&amd756_smbus);
373*4882a593Smuzhiyun if (error)
374*4882a593Smuzhiyun goto out_err;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun return 0;
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun out_err:
379*4882a593Smuzhiyun release_region(amd756_ioport, SMB_IOSIZE);
380*4882a593Smuzhiyun return error;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
amd756_remove(struct pci_dev * dev)383*4882a593Smuzhiyun static void amd756_remove(struct pci_dev *dev)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun i2c_del_adapter(&amd756_smbus);
386*4882a593Smuzhiyun release_region(amd756_ioport, SMB_IOSIZE);
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun static struct pci_driver amd756_driver = {
390*4882a593Smuzhiyun .name = "amd756_smbus",
391*4882a593Smuzhiyun .id_table = amd756_ids,
392*4882a593Smuzhiyun .probe = amd756_probe,
393*4882a593Smuzhiyun .remove = amd756_remove,
394*4882a593Smuzhiyun };
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun module_pci_driver(amd756_driver);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
399*4882a593Smuzhiyun MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
400*4882a593Smuzhiyun MODULE_LICENSE("GPL");
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun EXPORT_SYMBOL(amd756_smbus);
403