File Index
Files and directories
Sign in
FILE

nct7363.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/nct7363.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2023 Nuvoton Technology corporation.
  4 */
  5
  6#include <linux/bitfield.h>
  7#include <linux/bits.h>
  8#include <linux/err.h>
  9#include <linux/hwmon.h>
 10#include <linux/i2c.h>
 11#include <linux/module.h>
 12#include <linux/regmap.h>
 13#include <linux/slab.h>
 14
 15#define NCT7363_REG_FUNC_CFG_BASE(x)	(0x20 + (x))
 16#define NCT7363_REG_LSRS(x)		(0x34 + ((x) / 8))
 17#define NCT7363_REG_PWMEN_BASE(x)	(0x38 + (x))
 18#define NCT7363_REG_FANINEN_BASE(x)	(0x41 + (x))
 19#define NCT7363_REG_FANINX_HVAL(x)	(0x48 + ((x) * 2))
 20#define NCT7363_REG_FANINX_LVAL(x)	(0x49 + ((x) * 2))
 21#define NCT7363_REG_FANINX_HL(x)	(0x6C + ((x) * 2))
 22#define NCT7363_REG_FANINX_LL(x)	(0x6D + ((x) * 2))
 23#define NCT7363_REG_FSCPXDUTY(x)	(0x90 + ((x) * 2))
 24#define NCT7363_REG_FSCPXDIV(x)		(0x91 + ((x) * 2))
 25
 26#define PWM_SEL(x)			(BIT(0) << ((x) * 2))
 27#define FANIN_SEL(_x)			({typeof(_x) (x) = (_x); \
 28					 BIT(1) << (((x) < 8) ? \
 29					 (((x) + 8) * 2) : \
 30					 (((x) % 8) * 2)); })
 31#define ALARM_SEL(x, y)			((x) & (BIT((y) % 8)))
 32#define VALUE_TO_REG(x, y)		(((x) >> ((y) * 8)) & 0xFF)
 33
 34#define NCT7363_FANINX_LVAL_MASK	GENMASK(4, 0)
 35#define NCT7363_FANIN_MASK		GENMASK(12, 0)
 36
 37#define NCT7363_PWM_COUNT		16
 38
 39static inline unsigned int fan_from_reg(u16 val)
 40{
 41	if (val == NCT7363_FANIN_MASK || val == 0)
 42		return 0;
 43
 44	return (1350000UL / val);
 45}
 46
 47static const struct of_device_id nct7363_of_match[] = {
 48	{ .compatible = "nuvoton,nct7363", },
 49	{ .compatible = "nuvoton,nct7362", },
 50	{ }
 51};
 52MODULE_DEVICE_TABLE(of, nct7363_of_match);
 53
 54struct nct7363_data {
 55	struct regmap		*regmap;
 56
 57	u16			fanin_mask;
 58	u16			pwm_mask;
 59};
 60
 61static int nct7363_read_fan(struct device *dev, u32 attr, int channel,
 62			    long *val)
 63{
 64	struct nct7363_data *data = dev_get_drvdata(dev);
 65	unsigned int reg;
 66	u8 regval[2];
 67	int ret;
 68	u16 cnt;
 69
 70	switch (attr) {
 71	case hwmon_fan_input:
 72		/*
 73		 * High-byte register should be read first to latch
 74		 * synchronous low-byte value
 75		 */
 76		ret = regmap_bulk_read(data->regmap,
 77				       NCT7363_REG_FANINX_HVAL(channel),
 78				       &regval, 2);
 79		if (ret)
 80			return ret;
 81
 82		cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK);
 83		*val = fan_from_reg(cnt);
 84		return 0;
 85	case hwmon_fan_min:
 86		ret = regmap_bulk_read(data->regmap,
 87				       NCT7363_REG_FANINX_HL(channel),
 88				       &regval, 2);
 89		if (ret)
 90			return ret;
 91
 92		cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK);
 93		*val = fan_from_reg(cnt);
 94		return 0;
 95	case hwmon_fan_alarm:
 96		ret = regmap_read(data->regmap,
 97				  NCT7363_REG_LSRS(channel), &reg);
 98		if (ret)
 99			return ret;
100
101		*val = (long)ALARM_SEL(reg, channel) > 0 ? 1 : 0;
102		return 0;
103	default:
104		return -EOPNOTSUPP;
105	}
106}
107
108static int nct7363_write_fan(struct device *dev, u32 attr, int channel,
109			     long val)
110{
111	struct nct7363_data *data = dev_get_drvdata(dev);
112	u8 regval[2];
113	int ret;
114
115	if (val <= 0)
116		return -EINVAL;
117
118	switch (attr) {
119	case hwmon_fan_min:
120		val = clamp_val(DIV_ROUND_CLOSEST(1350000, val),
121				1, NCT7363_FANIN_MASK);
122		regval[0] = val >> 5;
123		regval[1] = val & NCT7363_FANINX_LVAL_MASK;
124
125		ret = regmap_bulk_write(data->regmap,
126					NCT7363_REG_FANINX_HL(channel),
127					regval, 2);
128		return ret;
129	default:
130		return -EOPNOTSUPP;
131	}
132}
133
134static umode_t nct7363_fan_is_visible(const void *_data, u32 attr, int channel)
135{
136	const struct nct7363_data *data = _data;
137
138	switch (attr) {
139	case hwmon_fan_input:
140	case hwmon_fan_alarm:
141		if (data->fanin_mask & BIT(channel))
142			return 0444;
143		break;
144	case hwmon_fan_min:
145		if (data->fanin_mask & BIT(channel))
146			return 0644;
147		break;
148	default:
149		break;
150	}
151
152	return 0;
153}
154
155static int nct7363_read_pwm(struct device *dev, u32 attr, int channel,
156			    long *val)
157{
158	struct nct7363_data *data = dev_get_drvdata(dev);
159	unsigned int regval;
160	int ret;
161
162	switch (attr) {
163	case hwmon_pwm_input:
164		ret = regmap_read(data->regmap,
165				  NCT7363_REG_FSCPXDUTY(channel), &regval);
166		if (ret)
167			return ret;
168
169		*val = (long)regval;
170		return 0;
171	default:
172		return -EOPNOTSUPP;
173	}
174}
175
176static int nct7363_write_pwm(struct device *dev, u32 attr, int channel,
177			     long val)
178{
179	struct nct7363_data *data = dev_get_drvdata(dev);
180	int ret;
181
182	switch (attr) {
183	case hwmon_pwm_input:
184		if (val < 0 || val > 255)
185			return -EINVAL;
186
187		ret = regmap_write(data->regmap,
188				   NCT7363_REG_FSCPXDUTY(channel), val);
189
190		return ret;
191
192	default:
193		return -EOPNOTSUPP;
194	}
195}
196
197static umode_t nct7363_pwm_is_visible(const void *_data, u32 attr, int channel)
198{
199	const struct nct7363_data *data = _data;
200
201	switch (attr) {
202	case hwmon_pwm_input:
203		if (data->pwm_mask & BIT(channel))
204			return 0644;
205		break;
206	default:
207		break;
208	}
209
210	return 0;
211}
212
213static int nct7363_read(struct device *dev, enum hwmon_sensor_types type,
214			u32 attr, int channel, long *val)
215{
216	switch (type) {
217	case hwmon_fan:
218		return nct7363_read_fan(dev, attr, channel, val);
219	case hwmon_pwm:
220		return nct7363_read_pwm(dev, attr, channel, val);
221	default:
222		return -EOPNOTSUPP;
223	}
224}
225
226static int nct7363_write(struct device *dev, enum hwmon_sensor_types type,
227			 u32 attr, int channel, long val)
228{
229	switch (type) {
230	case hwmon_fan:
231		return nct7363_write_fan(dev, attr, channel, val);
232	case hwmon_pwm:
233		return nct7363_write_pwm(dev, attr, channel, val);
234	default:
235		return -EOPNOTSUPP;
236	}
237}
238
239static umode_t nct7363_is_visible(const void *data,
240				  enum hwmon_sensor_types type,
241				  u32 attr, int channel)
242{
243	switch (type) {
244	case hwmon_fan:
245		return nct7363_fan_is_visible(data, attr, channel);
246	case hwmon_pwm:
247		return nct7363_pwm_is_visible(data, attr, channel);
248	default:
249		return 0;
250	}
251}
252
253static const struct hwmon_channel_info *nct7363_info[] = {
254	HWMON_CHANNEL_INFO(fan,
255			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
256			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
257			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
258			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
259			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
260			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
261			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
262			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
263			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
264			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
265			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
266			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
267			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
268			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
269			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
270			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
271	HWMON_CHANNEL_INFO(pwm,
272			   HWMON_PWM_INPUT,
273			   HWMON_PWM_INPUT,
274			   HWMON_PWM_INPUT,
275			   HWMON_PWM_INPUT,
276			   HWMON_PWM_INPUT,
277			   HWMON_PWM_INPUT,
278			   HWMON_PWM_INPUT,
279			   HWMON_PWM_INPUT,
280			   HWMON_PWM_INPUT,
281			   HWMON_PWM_INPUT,
282			   HWMON_PWM_INPUT,
283			   HWMON_PWM_INPUT,
284			   HWMON_PWM_INPUT,
285			   HWMON_PWM_INPUT,
286			   HWMON_PWM_INPUT,
287			   HWMON_PWM_INPUT),
288	NULL
289};
290
291static const struct hwmon_ops nct7363_hwmon_ops = {
292	.is_visible = nct7363_is_visible,
293	.read = nct7363_read,
294	.write = nct7363_write,
295};
296
297static const struct hwmon_chip_info nct7363_chip_info = {
298	.ops = &nct7363_hwmon_ops,
299	.info = nct7363_info,
300};
301
302static int nct7363_init_chip(struct nct7363_data *data)
303{
304	u32 func_config = 0;
305	int i, ret;
306
307	/* Pin Function Configuration */
308	for (i = 0; i < NCT7363_PWM_COUNT; i++) {
309		if (data->pwm_mask & BIT(i))
310			func_config |= PWM_SEL(i);
311		if (data->fanin_mask & BIT(i))
312			func_config |= FANIN_SEL(i);
313	}
314
315	for (i = 0; i < 4; i++) {
316		ret = regmap_write(data->regmap, NCT7363_REG_FUNC_CFG_BASE(i),
317				   VALUE_TO_REG(func_config, i));
318		if (ret < 0)
319			return ret;
320	}
321
322	/* PWM and FANIN Monitoring Enable */
323	for (i = 0; i < 2; i++) {
324		ret = regmap_write(data->regmap, NCT7363_REG_PWMEN_BASE(i),
325				   VALUE_TO_REG(data->pwm_mask, i));
326		if (ret < 0)
327			return ret;
328
329		ret = regmap_write(data->regmap, NCT7363_REG_FANINEN_BASE(i),
330				   VALUE_TO_REG(data->fanin_mask, i));
331		if (ret < 0)
332			return ret;
333	}
334
335	return 0;
336}
337
338static int nct7363_present_pwm_fanin(struct device *dev,
339				     struct device_node *child,
340				     struct nct7363_data *data)
341{
342	u8 fanin_ch[NCT7363_PWM_COUNT];
343	struct of_phandle_args args;
344	int ret, fanin_cnt;
345	u8 ch, index;
346
347	ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells",
348					 0, &args);
349	if (ret)
350		return ret;
351
352	of_node_put(args.np);
353	if (args.args[0] >= NCT7363_PWM_COUNT)
354		return -EINVAL;
355	data->pwm_mask |= BIT(args.args[0]);
356
357	fanin_cnt = of_property_count_u8_elems(child, "tach-ch");
358	if (fanin_cnt < 1 || fanin_cnt > NCT7363_PWM_COUNT)
359		return -EINVAL;
360
361	ret = of_property_read_u8_array(child, "tach-ch", fanin_ch, fanin_cnt);
362	if (ret)
363		return ret;
364
365	for (ch = 0; ch < fanin_cnt; ch++) {
366		index = fanin_ch[ch];
367		if (index >= NCT7363_PWM_COUNT)
368			return -EINVAL;
369		data->fanin_mask |= BIT(index);
370	}
371
372	return 0;
373}
374
375static bool nct7363_regmap_is_volatile(struct device *dev, unsigned int reg)
376{
377	switch (reg) {
378	case NCT7363_REG_LSRS(0) ... NCT7363_REG_LSRS(15):
379	case NCT7363_REG_FANINX_HVAL(0) ... NCT7363_REG_FANINX_LVAL(15):
380	case NCT7363_REG_FANINX_HL(0) ... NCT7363_REG_FANINX_LL(15):
381	case NCT7363_REG_FSCPXDUTY(0) ... NCT7363_REG_FSCPXDIV(15):
382		return true;
383	default:
384		return false;
385	}
386}
387
388static const struct regmap_config nct7363_regmap_config = {
389	.reg_bits = 8,
390	.val_bits = 8,
391	.use_single_read = true,
392	.use_single_write = true,
393	.cache_type = REGCACHE_MAPLE,
394	.volatile_reg = nct7363_regmap_is_volatile,
395};
396
397static int nct7363_probe(struct i2c_client *client)
398{
399	struct device *dev = &client->dev;
400	struct device_node *child;
401	struct nct7363_data *data;
402	struct device *hwmon_dev;
403	int ret;
404
405	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
406	if (!data)
407		return -ENOMEM;
408
409	data->regmap = devm_regmap_init_i2c(client, &nct7363_regmap_config);
410	if (IS_ERR(data->regmap))
411		return PTR_ERR(data->regmap);
412
413	for_each_child_of_node(dev->of_node, child) {
414		ret = nct7363_present_pwm_fanin(dev, child, data);
415		if (ret) {
416			of_node_put(child);
417			return ret;
418		}
419	}
420
421	/* Initialize the chip */
422	ret = nct7363_init_chip(data);
423	if (ret)
424		return ret;
425
426	hwmon_dev =
427		devm_hwmon_device_register_with_info(dev, client->name, data,
428						     &nct7363_chip_info, NULL);
429	return PTR_ERR_OR_ZERO(hwmon_dev);
430}
431
432static struct i2c_driver nct7363_driver = {
433	.class = I2C_CLASS_HWMON,
434	.driver = {
435		.name = "nct7363",
436		.of_match_table = nct7363_of_match,
437	},
438	.probe = nct7363_probe,
439};
440
441module_i2c_driver(nct7363_driver);
442
443MODULE_AUTHOR("CW Ho <cwho@nuvoton.com>");
444MODULE_AUTHOR("Ban Feng <kcfeng0@nuvoton.com>");
445MODULE_DESCRIPTION("NCT7363 Hardware Monitoring Driver");
446MODULE_LICENSE("GPL");
~/ Read-only file preview.