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