Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2006-2007 PA Semi, Inc
  4 *
  5 * SMBus host driver for PA Semi PWRficient
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/pci.h>
 10#include <linux/kernel.h>
 11#include <linux/stddef.h>
 12#include <linux/sched.h>
 13#include <linux/i2c.h>
 14#include <linux/delay.h>
 15#include <linux/slab.h>
 16#include <linux/io.h>
 17
 18static struct pci_driver pasemi_smb_driver;
 19
 20struct pasemi_smbus {
 21	struct pci_dev		*dev;
 22	struct i2c_adapter	 adapter;
 23	unsigned long		 base;
 24	int			 size;
 25};
 26
 27/* Register offsets */
 28#define REG_MTXFIFO	0x00
 29#define REG_MRXFIFO	0x04
 30#define REG_SMSTA	0x14
 31#define REG_CTL		0x1c
 32
 33/* Register defs */
 34#define MTXFIFO_READ	0x00000400
 35#define MTXFIFO_STOP	0x00000200
 36#define MTXFIFO_START	0x00000100
 37#define MTXFIFO_DATA_M	0x000000ff
 38
 39#define MRXFIFO_EMPTY	0x00000100
 40#define MRXFIFO_DATA_M	0x000000ff
 41
 42#define SMSTA_XEN	0x08000000
 43#define SMSTA_MTN	0x00200000
 44
 45#define CTL_MRR		0x00000400
 46#define CTL_MTR		0x00000200
 47#define CTL_CLK_M	0x000000ff
 48
 49#define CLK_100K_DIV	84
 50#define CLK_400K_DIV	21
 51
 52static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
 53{
 54	dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n",
 55		smbus->base + reg, val);
 56	outl(val, smbus->base + reg);
 57}
 58
 59static inline int reg_read(struct pasemi_smbus *smbus, int reg)
 60{
 61	int ret;
 62	ret = inl(smbus->base + reg);
 63	dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n",
 64		smbus->base + reg, ret);
 65	return ret;
 66}
 67
 68#define TXFIFO_WR(smbus, reg)	reg_write((smbus), REG_MTXFIFO, (reg))
 69#define RXFIFO_RD(smbus)	reg_read((smbus), REG_MRXFIFO)
 70
 71static void pasemi_smb_clear(struct pasemi_smbus *smbus)
 72{
 73	unsigned int status;
 74
 75	status = reg_read(smbus, REG_SMSTA);
 76	reg_write(smbus, REG_SMSTA, status);
 77}
 78
 79static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
 80{
 81	int timeout = 10;
 82	unsigned int status;
 83
 84	status = reg_read(smbus, REG_SMSTA);
 85
 86	while (!(status & SMSTA_XEN) && timeout--) {
 87		msleep(1);
 88		status = reg_read(smbus, REG_SMSTA);
 89	}
 90
 91	/* Got NACK? */
 92	if (status & SMSTA_MTN)
 93		return -ENXIO;
 94
 95	if (timeout < 0) {
 96		dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status);
 97		reg_write(smbus, REG_SMSTA, status);
 98		return -ETIME;
 99	}
100
101	/* Clear XEN */
102	reg_write(smbus, REG_SMSTA, SMSTA_XEN);
103
104	return 0;
105}
106
107static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,
108			       struct i2c_msg *msg, int stop)
109{
110	struct pasemi_smbus *smbus = adapter->algo_data;
111	int read, i, err;
112	u32 rd;
113
114	read = msg->flags & I2C_M_RD ? 1 : 0;
115
116	TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg));
117
118	if (read) {
119		TXFIFO_WR(smbus, msg->len | MTXFIFO_READ |
120				 (stop ? MTXFIFO_STOP : 0));
121
122		err = pasemi_smb_waitready(smbus);
123		if (err)
124			goto reset_out;
125
126		for (i = 0; i < msg->len; i++) {
127			rd = RXFIFO_RD(smbus);
128			if (rd & MRXFIFO_EMPTY) {
129				err = -ENODATA;
130				goto reset_out;
131			}
132			msg->buf[i] = rd & MRXFIFO_DATA_M;
133		}
134	} else {
135		for (i = 0; i < msg->len - 1; i++)
136			TXFIFO_WR(smbus, msg->buf[i]);
137
138		TXFIFO_WR(smbus, msg->buf[msg->len-1] |
139			  (stop ? MTXFIFO_STOP : 0));
140	}
141
142	return 0;
143
144 reset_out:
145	reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
146		  (CLK_100K_DIV & CTL_CLK_M)));
147	return err;
148}
149
150static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
151			   struct i2c_msg *msgs, int num)
152{
153	struct pasemi_smbus *smbus = adapter->algo_data;
154	int ret, i;
155
156	pasemi_smb_clear(smbus);
157
158	ret = 0;
159
160	for (i = 0; i < num && !ret; i++)
161		ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));
162
163	return ret ? ret : num;
164}
165
166static int pasemi_smb_xfer(struct i2c_adapter *adapter,
167		u16 addr, unsigned short flags, char read_write, u8 command,
168		int size, union i2c_smbus_data *data)
169{
170	struct pasemi_smbus *smbus = adapter->algo_data;
171	unsigned int rd;
172	int read_flag, err;
173	int len = 0, i;
174
175	/* All our ops take 8-bit shifted addresses */
176	addr <<= 1;
177	read_flag = read_write == I2C_SMBUS_READ;
178
179	pasemi_smb_clear(smbus);
180
181	switch (size) {
182	case I2C_SMBUS_QUICK:
183		TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START |
184			  MTXFIFO_STOP);
185		break;
186	case I2C_SMBUS_BYTE:
187		TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START);
188		if (read_write)
189			TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ);
190		else
191			TXFIFO_WR(smbus, MTXFIFO_STOP | command);
192		break;
193	case I2C_SMBUS_BYTE_DATA:
194		TXFIFO_WR(smbus, addr | MTXFIFO_START);
195		TXFIFO_WR(smbus, command);
196		if (read_write) {
197			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
198			TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP);
199		} else {
200			TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte);
201		}
202		break;
203	case I2C_SMBUS_WORD_DATA:
204		TXFIFO_WR(smbus, addr | MTXFIFO_START);
205		TXFIFO_WR(smbus, command);
206		if (read_write) {
207			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
208			TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP);
209		} else {
210			TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
211			TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8));
212		}
213		break;
214	case I2C_SMBUS_BLOCK_DATA:
215		TXFIFO_WR(smbus, addr | MTXFIFO_START);
216		TXFIFO_WR(smbus, command);
217		if (read_write) {
218			TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
219			TXFIFO_WR(smbus, 1 | MTXFIFO_READ);
220			rd = RXFIFO_RD(smbus);
221			len = min_t(u8, (rd & MRXFIFO_DATA_M),
222				    I2C_SMBUS_BLOCK_MAX);
223			TXFIFO_WR(smbus, len | MTXFIFO_READ |
224					 MTXFIFO_STOP);
225		} else {
226			len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
227			TXFIFO_WR(smbus, len);
228			for (i = 1; i < len; i++)
229				TXFIFO_WR(smbus, data->block[i]);
230			TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP);
231		}
232		break;
233	case I2C_SMBUS_PROC_CALL:
234		read_write = I2C_SMBUS_READ;
235		TXFIFO_WR(smbus, addr | MTXFIFO_START);
236		TXFIFO_WR(smbus, command);
237		TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
238		TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M);
239		TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
240		TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ);
241		break;
242	case I2C_SMBUS_BLOCK_PROC_CALL:
243		len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1);
244		read_write = I2C_SMBUS_READ;
245		TXFIFO_WR(smbus, addr | MTXFIFO_START);
246		TXFIFO_WR(smbus, command);
247		TXFIFO_WR(smbus, len);
248		for (i = 1; i <= len; i++)
249			TXFIFO_WR(smbus, data->block[i]);
250		TXFIFO_WR(smbus, addr | I2C_SMBUS_READ);
251		TXFIFO_WR(smbus, MTXFIFO_READ | 1);
252		rd = RXFIFO_RD(smbus);
253		len = min_t(u8, (rd & MRXFIFO_DATA_M),
254			    I2C_SMBUS_BLOCK_MAX - len);
255		TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP);
256		break;
257
258	default:
259		dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
260		return -EINVAL;
261	}
262
263	err = pasemi_smb_waitready(smbus);
264	if (err)
265		goto reset_out;
266
267	if (read_write == I2C_SMBUS_WRITE)
268		return 0;
269
270	switch (size) {
271	case I2C_SMBUS_BYTE:
272	case I2C_SMBUS_BYTE_DATA:
273		rd = RXFIFO_RD(smbus);
274		if (rd & MRXFIFO_EMPTY) {
275			err = -ENODATA;
276			goto reset_out;
277		}
278		data->byte = rd & MRXFIFO_DATA_M;
279		break;
280	case I2C_SMBUS_WORD_DATA:
281	case I2C_SMBUS_PROC_CALL:
282		rd = RXFIFO_RD(smbus);
283		if (rd & MRXFIFO_EMPTY) {
284			err = -ENODATA;
285			goto reset_out;
286		}
287		data->word = rd & MRXFIFO_DATA_M;
288		rd = RXFIFO_RD(smbus);
289		if (rd & MRXFIFO_EMPTY) {
290			err = -ENODATA;
291			goto reset_out;
292		}
293		data->word |= (rd & MRXFIFO_DATA_M) << 8;
294		break;
295	case I2C_SMBUS_BLOCK_DATA:
296	case I2C_SMBUS_BLOCK_PROC_CALL:
297		data->block[0] = len;
298		for (i = 1; i <= len; i ++) {
299			rd = RXFIFO_RD(smbus);
300			if (rd & MRXFIFO_EMPTY) {
301				err = -ENODATA;
302				goto reset_out;
303			}
304			data->block[i] = rd & MRXFIFO_DATA_M;
305		}
306		break;
307	}
308
309	return 0;
310
311 reset_out:
312	reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
313		  (CLK_100K_DIV & CTL_CLK_M)));
314	return err;
315}
316
317static u32 pasemi_smb_func(struct i2c_adapter *adapter)
318{
319	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
320	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
321	       I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
322	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C;
323}
324
325static const struct i2c_algorithm smbus_algorithm = {
326	.master_xfer	= pasemi_i2c_xfer,
327	.smbus_xfer	= pasemi_smb_xfer,
328	.functionality	= pasemi_smb_func,
329};
330
331static int pasemi_smb_probe(struct pci_dev *dev,
332				      const struct pci_device_id *id)
333{
334	struct pasemi_smbus *smbus;
335	int error;
336
337	if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
338		return -ENODEV;
339
340	smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL);
341	if (!smbus)
342		return -ENOMEM;
343
344	smbus->dev = dev;
345	smbus->base = pci_resource_start(dev, 0);
346	smbus->size = pci_resource_len(dev, 0);
347
348	if (!request_region(smbus->base, smbus->size,
349			    pasemi_smb_driver.name)) {
350		error = -EBUSY;
351		goto out_kfree;
352	}
353
354	smbus->adapter.owner = THIS_MODULE;
355	snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
356		 "PA Semi SMBus adapter at 0x%lx", smbus->base);
357	smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
358	smbus->adapter.algo = &smbus_algorithm;
359	smbus->adapter.algo_data = smbus;
360
361	/* set up the sysfs linkage to our parent device */
362	smbus->adapter.dev.parent = &dev->dev;
363
364	reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
365		  (CLK_100K_DIV & CTL_CLK_M)));
366
367	error = i2c_add_adapter(&smbus->adapter);
368	if (error)
369		goto out_release_region;
370
371	pci_set_drvdata(dev, smbus);
372
373	return 0;
374
375 out_release_region:
376	release_region(smbus->base, smbus->size);
377 out_kfree:
378	kfree(smbus);
379	return error;
380}
381
382static void pasemi_smb_remove(struct pci_dev *dev)
383{
384	struct pasemi_smbus *smbus = pci_get_drvdata(dev);
385
386	i2c_del_adapter(&smbus->adapter);
387	release_region(smbus->base, smbus->size);
388	kfree(smbus);
389}
390
391static const struct pci_device_id pasemi_smb_ids[] = {
392	{ PCI_DEVICE(0x1959, 0xa003) },
393	{ 0, }
394};
395
396MODULE_DEVICE_TABLE(pci, pasemi_smb_ids);
397
398static struct pci_driver pasemi_smb_driver = {
399	.name		= "i2c-pasemi",
400	.id_table	= pasemi_smb_ids,
401	.probe		= pasemi_smb_probe,
402	.remove		= pasemi_smb_remove,
403};
404
405module_pci_driver(pasemi_smb_driver);
406
407MODULE_LICENSE("GPL");
408MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
409MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver");