File Index
Files and directories
Sign in
FILE

powr1220.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/powr1220.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * powr1220.c - Driver for the Lattice POWR1220 programmable power supply
  4 * and monitor. Users can read all ADC inputs along with their labels
  5 * using the sysfs nodes.
  6 *
  7 * Copyright (c) 2014 Echo360 https://www.echo360.com
  8 * Scott Kanowitz <skanowitz@echo360.com> <scott.kanowitz@gmail.com>
  9 */
 10
 11#include <linux/module.h>
 12#include <linux/init.h>
 13#include <linux/slab.h>
 14#include <linux/jiffies.h>
 15#include <linux/i2c.h>
 16#include <linux/hwmon.h>
 17#include <linux/hwmon-sysfs.h>
 18#include <linux/err.h>
 19#include <linux/delay.h>
 20
 21#define ADC_STEP_MV			2
 22#define ADC_MAX_LOW_MEASUREMENT_MV	2000
 23
 24enum powr1xxx_chips { powr1014, powr1220 };
 25
 26enum powr1220_regs {
 27	VMON_STATUS0,
 28	VMON_STATUS1,
 29	VMON_STATUS2,
 30	OUTPUT_STATUS0,
 31	OUTPUT_STATUS1,
 32	OUTPUT_STATUS2,
 33	INPUT_STATUS,
 34	ADC_VALUE_LOW,
 35	ADC_VALUE_HIGH,
 36	ADC_MUX,
 37	UES_BYTE0,
 38	UES_BYTE1,
 39	UES_BYTE2,
 40	UES_BYTE3,
 41	GP_OUTPUT1,
 42	GP_OUTPUT2,
 43	GP_OUTPUT3,
 44	INPUT_VALUE,
 45	RESET,
 46	TRIM1_TRIM,
 47	TRIM2_TRIM,
 48	TRIM3_TRIM,
 49	TRIM4_TRIM,
 50	TRIM5_TRIM,
 51	TRIM6_TRIM,
 52	TRIM7_TRIM,
 53	TRIM8_TRIM,
 54	MAX_POWR1220_REGS
 55};
 56
 57enum powr1220_adc_values {
 58	VMON1,
 59	VMON2,
 60	VMON3,
 61	VMON4,
 62	VMON5,
 63	VMON6,
 64	VMON7,
 65	VMON8,
 66	VMON9,
 67	VMON10,
 68	VMON11,
 69	VMON12,
 70	VCCA,
 71	VCCINP,
 72	MAX_POWR1220_ADC_VALUES
 73};
 74
 75struct powr1220_data {
 76	struct i2c_client *client;
 77	u8 max_channels;
 78	bool adc_valid[MAX_POWR1220_ADC_VALUES];
 79	 /* the next value is in jiffies */
 80	unsigned long adc_last_updated[MAX_POWR1220_ADC_VALUES];
 81
 82	/* values */
 83	int adc_maxes[MAX_POWR1220_ADC_VALUES];
 84	int adc_values[MAX_POWR1220_ADC_VALUES];
 85};
 86
 87static const char * const input_names[] = {
 88	[VMON1]    = "vmon1",
 89	[VMON2]    = "vmon2",
 90	[VMON3]    = "vmon3",
 91	[VMON4]    = "vmon4",
 92	[VMON5]    = "vmon5",
 93	[VMON6]    = "vmon6",
 94	[VMON7]    = "vmon7",
 95	[VMON8]    = "vmon8",
 96	[VMON9]    = "vmon9",
 97	[VMON10]   = "vmon10",
 98	[VMON11]   = "vmon11",
 99	[VMON12]   = "vmon12",
100	[VCCA]     = "vcca",
101	[VCCINP]   = "vccinp",
102};
103
104/* Reads the specified ADC channel */
105static int powr1220_read_adc(struct device *dev, int ch_num)
106{
107	struct powr1220_data *data = dev_get_drvdata(dev);
108	int reading;
109	int result;
110	int adc_range = 0;
111
112	if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) ||
113	    !data->adc_valid[ch_num]) {
114		/*
115		 * figure out if we need to use the attenuator for
116		 * high inputs or inputs that we don't yet have a measurement
117		 * for. We dynamically set the attenuator depending on the
118		 * max reading.
119		 */
120		if (data->adc_maxes[ch_num] > ADC_MAX_LOW_MEASUREMENT_MV ||
121		    data->adc_maxes[ch_num] == 0)
122			adc_range = 1 << 4;
123
124		/* set the attenuator and mux */
125		result = i2c_smbus_write_byte_data(data->client, ADC_MUX,
126						   adc_range | ch_num);
127		if (result < 0)
128			return result;
129
130		/*
131		 * wait at least Tconvert time (200 us) for the
132		 * conversion to complete
133		 */
134		udelay(200);
135
136		/* get the ADC reading */
137		result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_LOW);
138		if (result < 0)
139			return result;
140
141		reading = result >> 4;
142
143		/* get the upper half of the reading */
144		result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_HIGH);
145		if (result < 0)
146			return result;
147
148		reading |= result << 4;
149
150		/* now convert the reading to a voltage */
151		reading *= ADC_STEP_MV;
152		data->adc_values[ch_num] = reading;
153		data->adc_valid[ch_num] = true;
154		data->adc_last_updated[ch_num] = jiffies;
155		result = reading;
156
157		if (reading > data->adc_maxes[ch_num])
158			data->adc_maxes[ch_num] = reading;
159	} else {
160		result = data->adc_values[ch_num];
161	}
162	return result;
163}
164
165static umode_t
166powr1220_is_visible(const void *data, enum hwmon_sensor_types type, u32
167		    attr, int channel)
168{
169	struct powr1220_data *chip_data = (struct powr1220_data *)data;
170
171	if (channel >= chip_data->max_channels)
172		return 0;
173
174	switch (type) {
175	case hwmon_in:
176		switch (attr) {
177		case hwmon_in_input:
178		case hwmon_in_highest:
179		case hwmon_in_label:
180			return 0444;
181		default:
182			break;
183		}
184		break;
185	default:
186		break;
187	}
188
189	return 0;
190}
191
192static int
193powr1220_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
194		     int channel, const char **str)
195{
196	switch (type) {
197	case hwmon_in:
198		switch (attr) {
199		case hwmon_in_label:
200			*str = input_names[channel];
201			return 0;
202		default:
203			return -EOPNOTSUPP;
204		}
205		break;
206	default:
207		return -EOPNOTSUPP;
208	}
209
210	return -EOPNOTSUPP;
211}
212
213static int
214powr1220_read(struct device *dev, enum hwmon_sensor_types type, u32
215	      attr, int channel, long *val)
216{
217	struct powr1220_data *data = dev_get_drvdata(dev);
218	int ret;
219
220	switch (type) {
221	case hwmon_in:
222		switch (attr) {
223		case hwmon_in_input:
224			ret = powr1220_read_adc(dev, channel);
225			if (ret < 0)
226				return ret;
227			*val = ret;
228			break;
229		case hwmon_in_highest:
230			*val = data->adc_maxes[channel];
231			break;
232		default:
233			return -EOPNOTSUPP;
234		}
235		break;
236	default:
237		return -EOPNOTSUPP;
238}
239
240	return 0;
241}
242
243static const struct hwmon_channel_info * const powr1220_info[] = {
244	HWMON_CHANNEL_INFO(in,
245			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
246			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
247			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
248			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
249			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
250			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
251			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
252			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
253			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
254			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
255			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
256			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
257			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL,
258			   HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL),
259
260	NULL
261};
262
263static const struct hwmon_ops powr1220_hwmon_ops = {
264	.read = powr1220_read,
265	.read_string = powr1220_read_string,
266	.is_visible = powr1220_is_visible,
267};
268
269static const struct hwmon_chip_info powr1220_chip_info = {
270	.ops = &powr1220_hwmon_ops,
271	.info = powr1220_info,
272};
273
274static int powr1220_probe(struct i2c_client *client)
275{
276	struct powr1220_data *data;
277	struct device *hwmon_dev;
278	enum powr1xxx_chips chip;
279
280	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
281		return -ENODEV;
282
283	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
284	if (!data)
285		return -ENOMEM;
286
287	chip = (uintptr_t)i2c_get_match_data(client);
288	switch (chip) {
289	case powr1014:
290		data->max_channels = 10;
291		break;
292	default:
293		data->max_channels = 12;
294		break;
295	}
296
297	data->client = client;
298
299	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
300							 client->name,
301							 data,
302							 &powr1220_chip_info,
303							 NULL);
304
305	return PTR_ERR_OR_ZERO(hwmon_dev);
306}
307
308static const struct i2c_device_id powr1220_ids[] = {
309	{ "powr1014", powr1014, },
310	{ "powr1220", powr1220, },
311	{ }
312};
313
314MODULE_DEVICE_TABLE(i2c, powr1220_ids);
315
316static struct i2c_driver powr1220_driver = {
317	.driver = {
318		.name	= "powr1220",
319	},
320	.probe		= powr1220_probe,
321	.id_table	= powr1220_ids,
322};
323
324module_i2c_driver(powr1220_driver);
325
326MODULE_AUTHOR("Scott Kanowitz");
327MODULE_DESCRIPTION("POWR1220 driver");
328MODULE_LICENSE("GPL");
~/ Read-only file preview.