Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * drivers/hwmon/nsa320-hwmon.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * ZyXEL NSA320 Media Servers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  * hardware monitoring
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  * Copyright (C) 2016 Adam Baker <linux@baker-net.org.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  * based on a board file driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  * Copyright (C) 2012 Peter Schildmann <linux@schildmann.info>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/bitops.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) #include <linux/gpio/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) #include <linux/hwmon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) #include <linux/hwmon-sysfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #include <linux/jiffies.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) /* Tests for error return values rely upon this value being < 0x80 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) #define MAGIC_NUMBER 0x55
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31)  * The Zyxel hwmon MCU is a Holtek HT46R065 that is factory programmed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32)  * to perform temperature and fan speed monitoring. It is read by taking
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33)  * the active pin low. The 32 bit output word is then clocked onto the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34)  * data line. The MSB of the data word is a magic nuber to indicate it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35)  * has been read correctly, the next byte is the fan speed (in hundreds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36)  * of RPM) and the last two bytes are the temperature (in tenths of a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37)  * degree)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) struct nsa320_hwmon {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 	struct mutex		update_lock;	/* lock GPIO operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 	unsigned long		last_updated;	/* jiffies */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 	unsigned long		mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 	struct gpio_desc	*act;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 	struct gpio_desc	*clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 	struct gpio_desc	*data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) enum nsa320_inputs {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) 	NSA320_TEMP = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	NSA320_FAN = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) static const char * const nsa320_input_names[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	[NSA320_TEMP] = "System Temperature",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	[NSA320_FAN] = "Chassis Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60)  * Although this protocol looks similar to SPI the long delay
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61)  * between the active (aka chip select) signal and the shorter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62)  * delay between clock pulses are needed for reliable operation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63)  * The delays provided are taken from the manufacturer kernel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64)  * testing suggest they probably incorporate a reasonable safety
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65)  * margin. (The single device tested became unreliable if the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66)  * delay was reduced to 1/10th of this value.)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) static s32 nsa320_hwmon_update(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	u32 mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	u32 mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	struct nsa320_hwmon *hwmon = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	mutex_lock(&hwmon->update_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	mcu_data = hwmon->mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 	if (time_after(jiffies, hwmon->last_updated + HZ) || mcu_data == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 		gpiod_set_value(hwmon->act, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 		msleep(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 		mcu_data = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 		for (mask = BIT(31); mask; mask >>= 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 			gpiod_set_value(hwmon->clk, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 			usleep_range(100, 200);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 			gpiod_set_value(hwmon->clk, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 			usleep_range(100, 200);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 			if (gpiod_get_value(hwmon->data))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 				mcu_data |= mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 		gpiod_set_value(hwmon->act, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 		dev_dbg(dev, "Read raw MCU data %08x\n", mcu_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 		if ((mcu_data >> 24) != MAGIC_NUMBER) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 			dev_dbg(dev, "Read invalid MCU data %08x\n", mcu_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 			mcu_data = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 		} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 			hwmon->mcu_data = mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 			hwmon->last_updated = jiffies;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	mutex_unlock(&hwmon->update_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 	return mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static ssize_t label_show(struct device *dev, struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 			  char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 	int channel = to_sensor_dev_attr(attr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	return sprintf(buf, "%s\n", nsa320_input_names[channel]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static ssize_t temp1_input_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 				struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	s32 mcu_data = nsa320_hwmon_update(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 	if (mcu_data < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 		return mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	return sprintf(buf, "%d\n", (mcu_data & 0xffff) * 100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static ssize_t fan1_input_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 			       struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	s32 mcu_data = nsa320_hwmon_update(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	if (mcu_data < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 		return mcu_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	return sprintf(buf, "%d\n", ((mcu_data & 0xff0000) >> 16) * 100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static SENSOR_DEVICE_ATTR_RO(temp1_label, label, NSA320_TEMP);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static DEVICE_ATTR_RO(temp1_input);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) static SENSOR_DEVICE_ATTR_RO(fan1_label, label, NSA320_FAN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static DEVICE_ATTR_RO(fan1_input);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static struct attribute *nsa320_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	&sensor_dev_attr_temp1_label.dev_attr.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	&dev_attr_temp1_input.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	&sensor_dev_attr_fan1_label.dev_attr.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	&dev_attr_fan1_input.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	NULL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) ATTRIBUTE_GROUPS(nsa320);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) static const struct of_device_id of_nsa320_hwmon_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	{ .compatible = "zyxel,nsa320-mcu", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	{ },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static int nsa320_hwmon_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	struct nsa320_hwmon	*hwmon;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 	struct device		*classdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	if (!hwmon)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	/* Look up the GPIO pins to use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 	hwmon->act = devm_gpiod_get(&pdev->dev, "act", GPIOD_OUT_LOW);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	if (IS_ERR(hwmon->act))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 		return PTR_ERR(hwmon->act);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 	hwmon->clk = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_HIGH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 	if (IS_ERR(hwmon->clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 		return PTR_ERR(hwmon->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	hwmon->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	if (IS_ERR(hwmon->data))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 		return PTR_ERR(hwmon->data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	mutex_init(&hwmon->update_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 	classdev = devm_hwmon_device_register_with_groups(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 					"nsa320", hwmon, nsa320_groups);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	return PTR_ERR_OR_ZERO(classdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) /* All allocations use devres so remove() is not needed. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) static struct platform_driver nsa320_hwmon_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	.probe = nsa320_hwmon_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	.driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 		.name = "nsa320-hwmon",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 		.of_match_table = of_match_ptr(of_nsa320_hwmon_match),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) module_platform_driver(nsa320_hwmon_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) MODULE_AUTHOR("Peter Schildmann <linux@schildmann.info>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) MODULE_DESCRIPTION("NSA320 Hardware Monitoring");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) MODULE_ALIAS("platform:nsa320-hwmon");