i2c-ali1563.c 11 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
Linus Torvalds's avatar
Linus Torvalds committed
3
4
5
 *	i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
 *
 *	Copyright (C) 2004 Patrick Mochel
6
 *		      2005 Rudolf Marek <r.marek@assembler.cz>
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 *	The 1563 southbridge is deceptively similar to the 1533, with a
 *	few notable exceptions. One of those happens to be the fact they
 *	upgraded the i2c core to be 2.0 compliant, and happens to be almost
 *	identical to the i2c controller found in the Intel 801 south
 *	bridges.
 *
 *	This driver is based on a mix of the 15x3, 1535, and i801 drivers,
 *	with a little help from the ALi 1563 spec.
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
22
#include <linux/acpi.h>
Linus Torvalds's avatar
Linus Torvalds committed
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

#define ALI1563_MAX_TIMEOUT	500
#define	ALI1563_SMBBA		0x80
#define ALI1563_SMB_IOEN	1
#define ALI1563_SMB_HOSTEN	2
#define ALI1563_SMB_IOSIZE	16

#define SMB_HST_STS	(ali1563_smba + 0)
#define SMB_HST_CNTL1	(ali1563_smba + 1)
#define SMB_HST_CNTL2	(ali1563_smba + 2)
#define SMB_HST_CMD	(ali1563_smba + 3)
#define SMB_HST_ADD	(ali1563_smba + 4)
#define SMB_HST_DAT0	(ali1563_smba + 5)
#define SMB_HST_DAT1	(ali1563_smba + 6)
#define SMB_BLK_DAT	(ali1563_smba + 7)

#define HST_STS_BUSY	0x01
#define HST_STS_INTR	0x02
#define HST_STS_DEVERR	0x04
#define HST_STS_BUSERR	0x08
#define HST_STS_FAIL	0x10
#define HST_STS_DONE	0x80
#define HST_STS_BAD	0x1c


#define HST_CNTL1_TIMEOUT	0x80
#define HST_CNTL1_LAST		0x40

#define HST_CNTL2_KILL		0x04
#define HST_CNTL2_START		0x40
#define HST_CNTL2_QUICK		0x00
#define HST_CNTL2_BYTE		0x01
#define HST_CNTL2_BYTE_DATA	0x02
#define HST_CNTL2_WORD_DATA	0x03
#define HST_CNTL2_BLOCK		0x05


60
#define HST_CNTL2_SIZEMASK	0x38
Linus Torvalds's avatar
Linus Torvalds committed
61

62
static struct pci_driver ali1563_pci_driver;
Linus Torvalds's avatar
Linus Torvalds committed
63
64
static unsigned short ali1563_smba;

65
static int ali1563_transaction(struct i2c_adapter *a, int size)
Linus Torvalds's avatar
Linus Torvalds committed
66
67
68
{
	u32 data;
	int timeout;
69
	int status = -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
73
74
75
76
77
78

	dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	data = inb_p(SMB_HST_STS);
	if (data & HST_STS_BAD) {
79
		dev_err(&a->dev, "ali1563: Trying to reset busy device\n");
80
		outb_p(data | HST_STS_BAD, SMB_HST_STS);
Linus Torvalds's avatar
Linus Torvalds committed
81
82
83
84
85
86
87
		data = inb_p(SMB_HST_STS);
		if (data & HST_STS_BAD)
			return -EBUSY;
	}
	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);

	timeout = ALI1563_MAX_TIMEOUT;
88
	do {
Linus Torvalds's avatar
Linus Torvalds committed
89
		msleep(1);
90
	} while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout);
Linus Torvalds's avatar
Linus Torvalds committed
91
92
93
94
95
96
97
98
99
100

	dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	if (timeout && !(data & HST_STS_BAD))
		return 0;

101
102
	if (!timeout) {
		dev_err(&a->dev, "Timeout - Trying to KILL transaction!\n");
Linus Torvalds's avatar
Linus Torvalds committed
103
		/* Issue 'kill' to host controller */
104
		outb_p(HST_CNTL2_KILL, SMB_HST_CNTL2);
105
		data = inb_p(SMB_HST_STS);
106
		status = -ETIMEDOUT;
107
	}
108
109

	/* device error - no response, ignore the autodetection case */
110
111
112
113
	if (data & HST_STS_DEVERR) {
		if (size != HST_CNTL2_QUICK)
			dev_err(&a->dev, "Device error!\n");
		status = -ENXIO;
114
115
116
117
118
	}
	/* bus collision */
	if (data & HST_STS_BUSERR) {
		dev_err(&a->dev, "Bus collision!\n");
		/* Issue timeout, hoping it helps */
119
		outb_p(HST_CNTL1_TIMEOUT, SMB_HST_CNTL1);
120
121
122
123
	}

	if (data & HST_STS_FAIL) {
		dev_err(&a->dev, "Cleaning fail after KILL!\n");
124
		outb_p(0x0, SMB_HST_CNTL2);
125
126
	}

127
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
128
129
}

130
static int ali1563_block_start(struct i2c_adapter *a)
Linus Torvalds's avatar
Linus Torvalds committed
131
132
133
{
	u32 data;
	int timeout;
134
	int status = -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
138
139
140
141
142
143

	dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	data = inb_p(SMB_HST_STS);
	if (data & HST_STS_BAD) {
144
145
		dev_warn(&a->dev, "ali1563: Trying to reset busy device\n");
		outb_p(data | HST_STS_BAD, SMB_HST_STS);
Linus Torvalds's avatar
Linus Torvalds committed
146
147
148
149
150
151
152
153
154
155
156
157
		data = inb_p(SMB_HST_STS);
		if (data & HST_STS_BAD)
			return -EBUSY;
	}

	/* Clear byte-ready bit */
	outb_p(data | HST_STS_DONE, SMB_HST_STS);

	/* Start transaction and wait for byte-ready bit to be set */
	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);

	timeout = ALI1563_MAX_TIMEOUT;
158
	do {
Linus Torvalds's avatar
Linus Torvalds committed
159
		msleep(1);
160
	} while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout);
Linus Torvalds's avatar
Linus Torvalds committed
161
162
163
164
165
166
167
168
169

	dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, "
		"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
		inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
		inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
		inb_p(SMB_HST_DAT1));

	if (timeout && !(data & HST_STS_BAD))
		return 0;
170
171
172
173
174
175
176

	if (timeout == 0)
		status = -ETIMEDOUT;

	if (data & HST_STS_DEVERR)
		status = -ENXIO;

177
	dev_err(&a->dev, "SMBus Error: %s%s%s%s%s\n",
178
		timeout ? "" : "Timeout ",
Linus Torvalds's avatar
Linus Torvalds committed
179
180
181
182
		data & HST_STS_FAIL ? "Transaction Failed " : "",
		data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
		data & HST_STS_DEVERR ? "Device Error " : "",
		!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
183
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
184
185
}

186
187
static int ali1563_block(struct i2c_adapter *a,
			 union i2c_smbus_data *data, u8 rw)
Linus Torvalds's avatar
Linus Torvalds committed
188
189
190
191
192
{
	int i, len;
	int error = 0;

	/* Do we need this? */
193
	outb_p(HST_CNTL1_LAST, SMB_HST_CNTL1);
Linus Torvalds's avatar
Linus Torvalds committed
194
195
196
197
198
199
200

	if (rw == I2C_SMBUS_WRITE) {
		len = data->block[0];
		if (len < 1)
			len = 1;
		else if (len > 32)
			len = 32;
201
202
		outb_p(len, SMB_HST_DAT0);
		outb_p(data->block[1], SMB_BLK_DAT);
Linus Torvalds's avatar
Linus Torvalds committed
203
204
205
206
207
208
209
210
	} else
		len = 32;

	outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2);

	for (i = 0; i < len; i++) {
		if (rw == I2C_SMBUS_WRITE) {
			outb_p(data->block[i + 1], SMB_BLK_DAT);
211
212
			error = ali1563_block_start(a);
			if (error)
Linus Torvalds's avatar
Linus Torvalds committed
213
214
				break;
		} else {
215
216
			error = ali1563_block_start(a);
			if (error)
Linus Torvalds's avatar
Linus Torvalds committed
217
218
219
220
221
222
223
224
225
226
227
228
				break;
			if (i == 0) {
				len = inb_p(SMB_HST_DAT0);
				if (len < 1)
					len = 1;
				else if (len > 32)
					len = 32;
			}
			data->block[i+1] = inb_p(SMB_BLK_DAT);
		}
	}
	/* Do we need this? */
229
	outb_p(HST_CNTL1_LAST, SMB_HST_CNTL1);
Linus Torvalds's avatar
Linus Torvalds committed
230
231
232
	return error;
}

233
static s32 ali1563_access(struct i2c_adapter *a, u16 addr,
Linus Torvalds's avatar
Linus Torvalds committed
234
			  unsigned short flags, char rw, u8 cmd,
235
			  int size, union i2c_smbus_data *data)
Linus Torvalds's avatar
Linus Torvalds committed
236
237
238
239
240
241
{
	int error = 0;
	int timeout;
	u32 reg;

	for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) {
242
243
		reg = inb_p(SMB_HST_STS);
		if (!(reg & HST_STS_BUSY))
Linus Torvalds's avatar
Linus Torvalds committed
244
245
246
			break;
	}
	if (!timeout)
247
248
		dev_warn(&a->dev, "SMBus not idle. HST_STS = %02x\n", reg);
	outb_p(0xff, SMB_HST_STS);
Linus Torvalds's avatar
Linus Torvalds committed
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

	/* Map the size to what the chip understands */
	switch (size) {
	case I2C_SMBUS_QUICK:
		size = HST_CNTL2_QUICK;
		break;
	case I2C_SMBUS_BYTE:
		size = HST_CNTL2_BYTE;
		break;
	case I2C_SMBUS_BYTE_DATA:
		size = HST_CNTL2_BYTE_DATA;
		break;
	case I2C_SMBUS_WORD_DATA:
		size = HST_CNTL2_WORD_DATA;
		break;
	case I2C_SMBUS_BLOCK_DATA:
		size = HST_CNTL2_BLOCK;
		break;
267
268
269
270
	default:
		dev_warn(&a->dev, "Unsupported transaction %d\n", size);
		error = -EOPNOTSUPP;
		goto Done;
Linus Torvalds's avatar
Linus Torvalds committed
271
272
273
	}

	outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD);
274
275
	outb_p((inb_p(SMB_HST_CNTL2) & ~HST_CNTL2_SIZEMASK) |
	       (size << 3), SMB_HST_CNTL2);
Linus Torvalds's avatar
Linus Torvalds committed
276
277

	/* Write the command register */
278

279
	switch (size) {
Linus Torvalds's avatar
Linus Torvalds committed
280
	case HST_CNTL2_BYTE:
281
		if (rw == I2C_SMBUS_WRITE)
282
283
			/* Beware it uses DAT0 register and not CMD! */
			outb_p(cmd, SMB_HST_DAT0);
Linus Torvalds's avatar
Linus Torvalds committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
		break;
	case HST_CNTL2_BYTE_DATA:
		outb_p(cmd, SMB_HST_CMD);
		if (rw == I2C_SMBUS_WRITE)
			outb_p(data->byte, SMB_HST_DAT0);
		break;
	case HST_CNTL2_WORD_DATA:
		outb_p(cmd, SMB_HST_CMD);
		if (rw == I2C_SMBUS_WRITE) {
			outb_p(data->word & 0xff, SMB_HST_DAT0);
			outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1);
		}
		break;
	case HST_CNTL2_BLOCK:
		outb_p(cmd, SMB_HST_CMD);
299
		error = ali1563_block(a, data, rw);
Linus Torvalds's avatar
Linus Torvalds committed
300
301
302
		goto Done;
	}

303
304
	error = ali1563_transaction(a, size);
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
		goto Done;

	if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK))
		goto Done;

	switch (size) {
	case HST_CNTL2_BYTE:	/* Result put in SMBHSTDAT0 */
		data->byte = inb_p(SMB_HST_DAT0);
		break;
	case HST_CNTL2_BYTE_DATA:
		data->byte = inb_p(SMB_HST_DAT0);
		break;
	case HST_CNTL2_WORD_DATA:
		data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8);
		break;
	}
Done:
	return error;
}

325
static u32 ali1563_func(struct i2c_adapter *a)
Linus Torvalds's avatar
Linus Torvalds committed
326
327
328
329
330
331
332
{
	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
	    I2C_FUNC_SMBUS_BLOCK_DATA;
}


333
static int ali1563_setup(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
334
335
336
{
	u16 ctrl;

337
	pci_read_config_word(dev, ALI1563_SMBBA, &ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
338
339
340
341
342

	/* SMB I/O Base in high 12 bits and must be aligned with the
	 * size of the I/O space. */
	ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1);
	if (!ali1563_smba) {
343
		dev_warn(&dev->dev, "ali1563_smba Uninitialized\n");
Linus Torvalds's avatar
Linus Torvalds committed
344
345
		goto Err;
	}
346
347
348
349
350
351
352
353
354
355
356
357

	/* Check if device is enabled */
	if (!(ctrl & ALI1563_SMB_HOSTEN)) {
		dev_warn(&dev->dev, "Host Controller not enabled\n");
		goto Err;
	}
	if (!(ctrl & ALI1563_SMB_IOEN)) {
		dev_warn(&dev->dev, "I/O space not enabled, trying manually\n");
		pci_write_config_word(dev, ALI1563_SMBBA,
				      ctrl | ALI1563_SMB_IOEN);
		pci_read_config_word(dev, ALI1563_SMBBA, &ctrl);
		if (!(ctrl & ALI1563_SMB_IOEN)) {
358
359
			dev_err(&dev->dev,
				"I/O space still not enabled, giving up\n");
360
361
362
363
			goto Err;
		}
	}

364
365
366
367
	if (acpi_check_region(ali1563_smba, ALI1563_SMB_IOSIZE,
			      ali1563_pci_driver.name))
		goto Err;

368
369
	if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE,
			    ali1563_pci_driver.name)) {
370
371
		dev_err(&dev->dev, "Could not allocate I/O space at 0x%04x\n",
			ali1563_smba);
Linus Torvalds's avatar
Linus Torvalds committed
372
373
		goto Err;
	}
374
	dev_info(&dev->dev, "Found ALi1563 SMBus at 0x%04x\n", ali1563_smba);
Linus Torvalds's avatar
Linus Torvalds committed
375
376
377
378
379
380
381
382

	return 0;
Err:
	return -ENODEV;
}

static void ali1563_shutdown(struct pci_dev *dev)
{
383
	release_region(ali1563_smba, ALI1563_SMB_IOSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
384
385
}

386
static const struct i2c_algorithm ali1563_algorithm = {
Linus Torvalds's avatar
Linus Torvalds committed
387
388
389
390
391
392
	.smbus_xfer	= ali1563_access,
	.functionality	= ali1563_func,
};

static struct i2c_adapter ali1563_adapter = {
	.owner	= THIS_MODULE,
393
	.class	= I2C_CLASS_HWMON | I2C_CLASS_SPD,
Linus Torvalds's avatar
Linus Torvalds committed
394
395
396
	.algo	= &ali1563_algorithm,
};

397
398
static int ali1563_probe(struct pci_dev *dev,
			 const struct pci_device_id *id_table)
Linus Torvalds's avatar
Linus Torvalds committed
399
400
401
{
	int error;

402
403
	error = ali1563_setup(dev);
	if (error)
404
		goto exit;
Linus Torvalds's avatar
Linus Torvalds committed
405
	ali1563_adapter.dev.parent = &dev->dev;
406
407
	snprintf(ali1563_adapter.name, sizeof(ali1563_adapter.name),
		 "SMBus ALi 1563 Adapter @ %04x", ali1563_smba);
408
409
	error = i2c_add_adapter(&ali1563_adapter);
	if (error)
410
411
412
413
414
415
416
		goto exit_shutdown;
	return 0;

exit_shutdown:
	ali1563_shutdown(dev);
exit:
	dev_warn(&dev->dev, "ALi1563 SMBus probe failed (%d)\n", error);
Linus Torvalds's avatar
Linus Torvalds committed
417
418
419
	return error;
}

420
static void ali1563_remove(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
421
422
423
424
425
{
	i2c_del_adapter(&ali1563_adapter);
	ali1563_shutdown(dev);
}

426
static const struct pci_device_id ali1563_id_table[] = {
Linus Torvalds's avatar
Linus Torvalds committed
427
428
429
430
	{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) },
	{},
};

431
MODULE_DEVICE_TABLE(pci, ali1563_id_table);
Linus Torvalds's avatar
Linus Torvalds committed
432
433

static struct pci_driver ali1563_pci_driver = {
434
	.name		= "ali1563_smbus",
Linus Torvalds's avatar
Linus Torvalds committed
435
	.id_table	= ali1563_id_table,
436
	.probe		= ali1563_probe,
437
	.remove		= ali1563_remove,
Linus Torvalds's avatar
Linus Torvalds committed
438
439
};

Axel Lin's avatar
Axel Lin committed
440
module_pci_driver(ali1563_pci_driver);
Linus Torvalds's avatar
Linus Torvalds committed
441
442

MODULE_LICENSE("GPL");