zl6100.c revision 200855e52db1b1834121ba57fbd89c5b4911e02c
1/*
2 * Hardware monitoring driver for ZL6100 and compatibles
3 *
4 * Copyright (c) 2011 Ericsson AB.
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 as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/err.h>
25#include <linux/slab.h>
26#include <linux/i2c.h>
27#include <linux/ktime.h>
28#include <linux/delay.h>
29#include "pmbus.h"
30
31enum chips { zl2004, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105 };
32
33struct zl6100_data {
34	int id;
35	ktime_t access;		/* chip access time */
36	struct pmbus_driver_info info;
37};
38
39#define to_zl6100_data(x)  container_of(x, struct zl6100_data, info)
40
41#define ZL6100_DEVICE_ID		0xe4
42
43#define ZL6100_WAIT_TIME		1000	/* uS	*/
44
45static ushort delay = ZL6100_WAIT_TIME;
46module_param(delay, ushort, 0644);
47MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
48
49/* Some chips need a delay between accesses */
50static inline void zl6100_wait(const struct zl6100_data *data)
51{
52	if (delay) {
53		s64 delta = ktime_us_delta(ktime_get(), data->access);
54		if (delta < delay)
55			udelay(delay - delta);
56	}
57}
58
59static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
60{
61	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
62	struct zl6100_data *data = to_zl6100_data(info);
63	int ret;
64
65	if (page || reg >= PMBUS_VIRT_BASE)
66		return -ENXIO;
67
68	zl6100_wait(data);
69	ret = pmbus_read_word_data(client, page, reg);
70	data->access = ktime_get();
71
72	return ret;
73}
74
75static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
76{
77	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
78	struct zl6100_data *data = to_zl6100_data(info);
79	int ret;
80
81	if (page > 0)
82		return -ENXIO;
83
84	zl6100_wait(data);
85	ret = pmbus_read_byte_data(client, page, reg);
86	data->access = ktime_get();
87
88	return ret;
89}
90
91static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
92				  u16 word)
93{
94	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
95	struct zl6100_data *data = to_zl6100_data(info);
96	int ret;
97
98	if (page || reg >= PMBUS_VIRT_BASE)
99		return -ENXIO;
100
101	zl6100_wait(data);
102	ret = pmbus_write_word_data(client, page, reg, word);
103	data->access = ktime_get();
104
105	return ret;
106}
107
108static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
109{
110	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
111	struct zl6100_data *data = to_zl6100_data(info);
112	int ret;
113
114	if (page > 0)
115		return -ENXIO;
116
117	zl6100_wait(data);
118	ret = pmbus_write_byte(client, page, value);
119	data->access = ktime_get();
120
121	return ret;
122}
123
124static const struct i2c_device_id zl6100_id[] = {
125	{"zl2004", zl2004},
126	{"zl2006", zl2006},
127	{"zl2008", zl2008},
128	{"zl2105", zl2105},
129	{"zl2106", zl2106},
130	{"zl6100", zl6100},
131	{"zl6105", zl6105},
132	{ }
133};
134MODULE_DEVICE_TABLE(i2c, zl6100_id);
135
136static int zl6100_probe(struct i2c_client *client,
137			const struct i2c_device_id *id)
138{
139	int ret;
140	struct zl6100_data *data;
141	struct pmbus_driver_info *info;
142	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
143	const struct i2c_device_id *mid;
144
145	if (!i2c_check_functionality(client->adapter,
146				     I2C_FUNC_SMBUS_READ_BYTE_DATA
147				     | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
148		return -ENODEV;
149
150	ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
151					device_id);
152	if (ret < 0) {
153		dev_err(&client->dev, "Failed to read device ID\n");
154		return ret;
155	}
156	device_id[ret] = '\0';
157	dev_info(&client->dev, "Device ID %s\n", device_id);
158
159	mid = NULL;
160	for (mid = zl6100_id; mid->name[0]; mid++) {
161		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
162			break;
163	}
164	if (!mid->name[0]) {
165		dev_err(&client->dev, "Unsupported device\n");
166		return -ENODEV;
167	}
168	if (id->driver_data != mid->driver_data)
169		dev_notice(&client->dev,
170			   "Device mismatch: Configured %s, detected %s\n",
171			   id->name, mid->name);
172
173	data = kzalloc(sizeof(struct zl6100_data), GFP_KERNEL);
174	if (!data)
175		return -ENOMEM;
176
177	data->id = mid->driver_data;
178
179	/*
180	 * ZL2008, ZL2105, and ZL6100 are known to require a wait time
181	 * between I2C accesses. ZL2004 and ZL6105 are known to be safe.
182	 *
183	 * Only clear the wait time for chips known to be safe. The wait time
184	 * can be cleared later for additional chips if tests show that it
185	 * is not needed (in other words, better be safe than sorry).
186	 */
187	if (data->id == zl2004 || data->id == zl6105)
188		delay = 0;
189
190	/*
191	 * Since there was a direct I2C device access above, wait before
192	 * accessing the chip again.
193	 * Set the timestamp, wait, then set it again. This should provide
194	 * enough buffer time to be safe.
195	 */
196	data->access = ktime_get();
197	zl6100_wait(data);
198	data->access = ktime_get();
199
200	info = &data->info;
201
202	info->pages = 1;
203	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
204	  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
205	  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
206	  | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
207
208	info->read_word_data = zl6100_read_word_data;
209	info->read_byte_data = zl6100_read_byte_data;
210	info->write_word_data = zl6100_write_word_data;
211	info->write_byte = zl6100_write_byte;
212
213	ret = pmbus_do_probe(client, mid, info);
214	if (ret)
215		goto err_mem;
216	return 0;
217
218err_mem:
219	kfree(data);
220	return ret;
221}
222
223static int zl6100_remove(struct i2c_client *client)
224{
225	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
226	const struct zl6100_data *data = to_zl6100_data(info);
227
228	pmbus_do_remove(client);
229	kfree(data);
230	return 0;
231}
232
233static struct i2c_driver zl6100_driver = {
234	.driver = {
235		   .name = "zl6100",
236		   },
237	.probe = zl6100_probe,
238	.remove = zl6100_remove,
239	.id_table = zl6100_id,
240};
241
242static int __init zl6100_init(void)
243{
244	return i2c_add_driver(&zl6100_driver);
245}
246
247static void __exit zl6100_exit(void)
248{
249	i2c_del_driver(&zl6100_driver);
250}
251
252MODULE_AUTHOR("Guenter Roeck");
253MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
254MODULE_LICENSE("GPL");
255module_init(zl6100_init);
256module_exit(zl6100_exit);
257