Linux Audio

Check our new training course

Loading...
  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, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
 32	     zl9101, zl9117 };
 33
 34struct zl6100_data {
 35	int id;
 36	ktime_t access;		/* chip access time */
 37	int delay;		/* Delay between chip accesses in uS */
 38	struct pmbus_driver_info info;
 39};
 40
 41#define to_zl6100_data(x)  container_of(x, struct zl6100_data, info)
 42
 43#define ZL6100_MFR_CONFIG		0xd0
 44#define ZL6100_DEVICE_ID		0xe4
 45
 46#define ZL6100_MFR_XTEMP_ENABLE		(1 << 7)
 47
 48#define ZL6100_WAIT_TIME		1000	/* uS	*/
 49
 50static ushort delay = ZL6100_WAIT_TIME;
 51module_param(delay, ushort, 0644);
 52MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
 53
 54/* Some chips need a delay between accesses */
 55static inline void zl6100_wait(const struct zl6100_data *data)
 56{
 57	if (data->delay) {
 58		s64 delta = ktime_us_delta(ktime_get(), data->access);
 59		if (delta < data->delay)
 60			udelay(data->delay - delta);
 61	}
 62}
 63
 64static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
 65{
 66	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
 67	struct zl6100_data *data = to_zl6100_data(info);
 68	int ret;
 69
 70	if (page || reg >= PMBUS_VIRT_BASE)
 71		return -ENXIO;
 72
 73	if (data->id == zl2005) {
 74		/*
 75		 * Limit register detection is not reliable on ZL2005.
 76		 * Make sure registers are not erroneously detected.
 77		 */
 78		switch (reg) {
 79		case PMBUS_VOUT_OV_WARN_LIMIT:
 80		case PMBUS_VOUT_UV_WARN_LIMIT:
 81		case PMBUS_IOUT_OC_WARN_LIMIT:
 82			return -ENXIO;
 83		}
 84	}
 85
 86	zl6100_wait(data);
 87	ret = pmbus_read_word_data(client, page, reg);
 88	data->access = ktime_get();
 89
 90	return ret;
 91}
 92
 93static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
 94{
 95	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
 96	struct zl6100_data *data = to_zl6100_data(info);
 97	int ret;
 98
 99	if (page > 0)
100		return -ENXIO;
101
102	zl6100_wait(data);
103	ret = pmbus_read_byte_data(client, page, reg);
104	data->access = ktime_get();
105
106	return ret;
107}
108
109static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
110				  u16 word)
111{
112	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
113	struct zl6100_data *data = to_zl6100_data(info);
114	int ret;
115
116	if (page || reg >= PMBUS_VIRT_BASE)
117		return -ENXIO;
118
119	zl6100_wait(data);
120	ret = pmbus_write_word_data(client, page, reg, word);
121	data->access = ktime_get();
122
123	return ret;
124}
125
126static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
127{
128	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
129	struct zl6100_data *data = to_zl6100_data(info);
130	int ret;
131
132	if (page > 0)
133		return -ENXIO;
134
135	zl6100_wait(data);
136	ret = pmbus_write_byte(client, page, value);
137	data->access = ktime_get();
138
139	return ret;
140}
141
142static const struct i2c_device_id zl6100_id[] = {
143	{"bmr450", zl2005},
144	{"bmr451", zl2005},
145	{"bmr462", zl2008},
146	{"bmr463", zl2008},
147	{"bmr464", zl2008},
148	{"zl2004", zl2004},
149	{"zl2005", zl2005},
150	{"zl2006", zl2006},
151	{"zl2008", zl2008},
152	{"zl2105", zl2105},
153	{"zl2106", zl2106},
154	{"zl6100", zl6100},
155	{"zl6105", zl6105},
156	{"zl9101", zl9101},
157	{"zl9117", zl9117},
158	{ }
159};
160MODULE_DEVICE_TABLE(i2c, zl6100_id);
161
162static int zl6100_probe(struct i2c_client *client,
163			const struct i2c_device_id *id)
164{
165	int ret;
166	struct zl6100_data *data;
167	struct pmbus_driver_info *info;
168	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
169	const struct i2c_device_id *mid;
170
171	if (!i2c_check_functionality(client->adapter,
172				     I2C_FUNC_SMBUS_READ_WORD_DATA
173				     | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
174		return -ENODEV;
175
176	ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
177					device_id);
178	if (ret < 0) {
179		dev_err(&client->dev, "Failed to read device ID\n");
180		return ret;
181	}
182	device_id[ret] = '\0';
183	dev_info(&client->dev, "Device ID %s\n", device_id);
184
185	mid = NULL;
186	for (mid = zl6100_id; mid->name[0]; mid++) {
187		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
188			break;
189	}
190	if (!mid->name[0]) {
191		dev_err(&client->dev, "Unsupported device\n");
192		return -ENODEV;
193	}
194	if (id->driver_data != mid->driver_data)
195		dev_notice(&client->dev,
196			   "Device mismatch: Configured %s, detected %s\n",
197			   id->name, mid->name);
198
199	data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),
200			    GFP_KERNEL);
201	if (!data)
202		return -ENOMEM;
203
204	data->id = mid->driver_data;
205
206	/*
207	 * According to information from the chip vendor, all currently
208	 * supported chips are known to require a wait time between I2C
209	 * accesses.
210	 */
211	data->delay = delay;
212
213	/*
214	 * Since there was a direct I2C device access above, wait before
215	 * accessing the chip again.
216	 */
217	data->access = ktime_get();
218	zl6100_wait(data);
219
220	info = &data->info;
221
222	info->pages = 1;
223	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
224	  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
225	  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
226	  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
227
228	ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
229	if (ret < 0)
230		return ret;
231
232	if (ret & ZL6100_MFR_XTEMP_ENABLE)
233		info->func[0] |= PMBUS_HAVE_TEMP2;
234
235	data->access = ktime_get();
236	zl6100_wait(data);
237
238	info->read_word_data = zl6100_read_word_data;
239	info->read_byte_data = zl6100_read_byte_data;
240	info->write_word_data = zl6100_write_word_data;
241	info->write_byte = zl6100_write_byte;
242
243	return pmbus_do_probe(client, mid, info);
244}
245
246static struct i2c_driver zl6100_driver = {
247	.driver = {
248		   .name = "zl6100",
249		   },
250	.probe = zl6100_probe,
251	.remove = pmbus_do_remove,
252	.id_table = zl6100_id,
253};
254
255module_i2c_driver(zl6100_driver);
256
257MODULE_AUTHOR("Guenter Roeck");
258MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
259MODULE_LICENSE("GPL");