xref: /openbmc/linux/drivers/hwmon/ultra45_env.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a80b10ccSGuenter Roeck /*
3a80b10ccSGuenter Roeck  * ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor.
4e0418088SDavid S. Miller  *
5e0418088SDavid S. Miller  * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
6e0418088SDavid S. Miller  */
7e0418088SDavid S. Miller 
8e0418088SDavid S. Miller #include <linux/kernel.h>
9e0418088SDavid S. Miller #include <linux/types.h>
10e0418088SDavid S. Miller #include <linux/slab.h>
1131a1a152SPaul Gortmaker #include <linux/module.h>
12*39f03438SRob Herring #include <linux/of.h>
13*39f03438SRob Herring #include <linux/platform_device.h>
14e0418088SDavid S. Miller #include <linux/io.h>
15e0418088SDavid S. Miller #include <linux/hwmon.h>
16e0418088SDavid S. Miller #include <linux/hwmon-sysfs.h>
17fa845740SJean Delvare #include <linux/err.h>
18e0418088SDavid S. Miller 
19e0418088SDavid S. Miller #define DRV_MODULE_VERSION	"0.1"
20e0418088SDavid S. Miller 
21e0418088SDavid S. Miller MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
22e0418088SDavid S. Miller MODULE_DESCRIPTION("Ultra45 environmental monitor driver");
23e0418088SDavid S. Miller MODULE_LICENSE("GPL");
24e0418088SDavid S. Miller MODULE_VERSION(DRV_MODULE_VERSION);
25e0418088SDavid S. Miller 
26e0418088SDavid S. Miller /* PIC device registers */
27e0418088SDavid S. Miller #define REG_CMD		0x00UL
28e0418088SDavid S. Miller #define  REG_CMD_RESET	0x80
29e0418088SDavid S. Miller #define  REG_CMD_ESTAR	0x01
30e0418088SDavid S. Miller #define REG_STAT	0x01UL
31e0418088SDavid S. Miller #define  REG_STAT_FWVER	0xf0
32e0418088SDavid S. Miller #define  REG_STAT_TGOOD	0x08
33e0418088SDavid S. Miller #define  REG_STAT_STALE	0x04
34e0418088SDavid S. Miller #define  REG_STAT_BUSY	0x02
35e0418088SDavid S. Miller #define  REG_STAT_FAULT	0x01
36e0418088SDavid S. Miller #define REG_DATA	0x40UL
37e0418088SDavid S. Miller #define REG_ADDR	0x41UL
38e0418088SDavid S. Miller #define REG_SIZE	0x42UL
39e0418088SDavid S. Miller 
40e0418088SDavid S. Miller /* Registers accessed indirectly via REG_DATA/REG_ADDR */
41e0418088SDavid S. Miller #define IREG_FAN0		0x00
42e0418088SDavid S. Miller #define IREG_FAN1		0x01
43e0418088SDavid S. Miller #define IREG_FAN2		0x02
44e0418088SDavid S. Miller #define IREG_FAN3		0x03
45e0418088SDavid S. Miller #define IREG_FAN4		0x04
46e0418088SDavid S. Miller #define IREG_FAN5		0x05
47e0418088SDavid S. Miller #define IREG_LCL_TEMP		0x06
48e0418088SDavid S. Miller #define IREG_RMT1_TEMP		0x07
49e0418088SDavid S. Miller #define IREG_RMT2_TEMP		0x08
50e0418088SDavid S. Miller #define IREG_RMT3_TEMP		0x09
51e0418088SDavid S. Miller #define IREG_LM95221_TEMP	0x0a
52e0418088SDavid S. Miller #define IREG_FIRE_TEMP		0x0b
53e0418088SDavid S. Miller #define IREG_LSI1064_TEMP	0x0c
54e0418088SDavid S. Miller #define IREG_FRONT_TEMP		0x0d
55e0418088SDavid S. Miller #define IREG_FAN_STAT		0x0e
56e0418088SDavid S. Miller #define IREG_VCORE0		0x0f
57e0418088SDavid S. Miller #define IREG_VCORE1		0x10
58e0418088SDavid S. Miller #define IREG_VMEM0		0x11
59e0418088SDavid S. Miller #define IREG_VMEM1		0x12
60e0418088SDavid S. Miller #define IREG_PSU_TEMP		0x13
61e0418088SDavid S. Miller 
62e0418088SDavid S. Miller struct env {
63e0418088SDavid S. Miller 	void __iomem	*regs;
64e0418088SDavid S. Miller 	spinlock_t	lock;
65e0418088SDavid S. Miller 
66e0418088SDavid S. Miller 	struct device	*hwmon_dev;
67e0418088SDavid S. Miller };
68e0418088SDavid S. Miller 
env_read(struct env * p,u8 ireg)69e0418088SDavid S. Miller static u8 env_read(struct env *p, u8 ireg)
70e0418088SDavid S. Miller {
71e0418088SDavid S. Miller 	u8 ret;
72e0418088SDavid S. Miller 
73e0418088SDavid S. Miller 	spin_lock(&p->lock);
74e0418088SDavid S. Miller 	writeb(ireg, p->regs + REG_ADDR);
75e0418088SDavid S. Miller 	ret = readb(p->regs + REG_DATA);
76e0418088SDavid S. Miller 	spin_unlock(&p->lock);
77e0418088SDavid S. Miller 
78e0418088SDavid S. Miller 	return ret;
79e0418088SDavid S. Miller }
80e0418088SDavid S. Miller 
env_write(struct env * p,u8 ireg,u8 val)81e0418088SDavid S. Miller static void env_write(struct env *p, u8 ireg, u8 val)
82e0418088SDavid S. Miller {
83e0418088SDavid S. Miller 	spin_lock(&p->lock);
84e0418088SDavid S. Miller 	writeb(ireg, p->regs + REG_ADDR);
85e0418088SDavid S. Miller 	writeb(val, p->regs + REG_DATA);
86e0418088SDavid S. Miller 	spin_unlock(&p->lock);
87e0418088SDavid S. Miller }
88e0418088SDavid S. Miller 
89a80b10ccSGuenter Roeck /*
90a80b10ccSGuenter Roeck  * There seems to be a adr7462 providing these values, thus a lot
91e0418088SDavid S. Miller  * of these calculations are borrowed from the adt7470 driver.
92e0418088SDavid S. Miller  */
93e0418088SDavid S. Miller #define FAN_PERIOD_TO_RPM(x)	((90000 * 60) / (x))
94e0418088SDavid S. Miller #define FAN_RPM_TO_PERIOD	FAN_PERIOD_TO_RPM
95e0418088SDavid S. Miller #define FAN_PERIOD_INVALID	(0xff << 8)
96e0418088SDavid S. Miller #define FAN_DATA_VALID(x)	((x) && (x) != FAN_PERIOD_INVALID)
97e0418088SDavid S. Miller 
show_fan_speed(struct device * dev,struct device_attribute * attr,char * buf)98a80b10ccSGuenter Roeck static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr,
99a80b10ccSGuenter Roeck 			      char *buf)
100e0418088SDavid S. Miller {
101e0418088SDavid S. Miller 	int fan_nr = to_sensor_dev_attr(attr)->index;
102e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
103e0418088SDavid S. Miller 	int rpm, period;
104e0418088SDavid S. Miller 	u8 val;
105e0418088SDavid S. Miller 
106e0418088SDavid S. Miller 	val = env_read(p, IREG_FAN0 + fan_nr);
107e0418088SDavid S. Miller 	period = (int) val << 8;
108e0418088SDavid S. Miller 	if (FAN_DATA_VALID(period))
109e0418088SDavid S. Miller 		rpm = FAN_PERIOD_TO_RPM(period);
110e0418088SDavid S. Miller 	else
111e0418088SDavid S. Miller 		rpm = 0;
112e0418088SDavid S. Miller 
113e0418088SDavid S. Miller 	return sprintf(buf, "%d\n", rpm);
114e0418088SDavid S. Miller }
115e0418088SDavid S. Miller 
set_fan_speed(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)116e0418088SDavid S. Miller static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr,
117e0418088SDavid S. Miller 			     const char *buf, size_t count)
118e0418088SDavid S. Miller {
119e0418088SDavid S. Miller 	int fan_nr = to_sensor_dev_attr(attr)->index;
120a80b10ccSGuenter Roeck 	unsigned long rpm;
121e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
122e0418088SDavid S. Miller 	int period;
123e0418088SDavid S. Miller 	u8 val;
124a80b10ccSGuenter Roeck 	int err;
125a80b10ccSGuenter Roeck 
126a80b10ccSGuenter Roeck 	err = kstrtoul(buf, 10, &rpm);
127a80b10ccSGuenter Roeck 	if (err)
128a80b10ccSGuenter Roeck 		return err;
129e0418088SDavid S. Miller 
130e0418088SDavid S. Miller 	if (!rpm)
131e0418088SDavid S. Miller 		return -EINVAL;
132e0418088SDavid S. Miller 
133e0418088SDavid S. Miller 	period = FAN_RPM_TO_PERIOD(rpm);
134e0418088SDavid S. Miller 	val = period >> 8;
135e0418088SDavid S. Miller 	env_write(p, IREG_FAN0 + fan_nr, val);
136e0418088SDavid S. Miller 
137e0418088SDavid S. Miller 	return count;
138e0418088SDavid S. Miller }
139e0418088SDavid S. Miller 
show_fan_fault(struct device * dev,struct device_attribute * attr,char * buf)140a80b10ccSGuenter Roeck static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr,
141a80b10ccSGuenter Roeck 			      char *buf)
142e0418088SDavid S. Miller {
143e0418088SDavid S. Miller 	int fan_nr = to_sensor_dev_attr(attr)->index;
144e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
145e0418088SDavid S. Miller 	u8 val = env_read(p, IREG_FAN_STAT);
146e0418088SDavid S. Miller 	return sprintf(buf, "%d\n", (val & (1 << fan_nr)) ? 1 : 0);
147e0418088SDavid S. Miller }
148e0418088SDavid S. Miller 
149e0418088SDavid S. Miller #define fan(index)							\
150e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(fan##index##_speed, S_IRUGO | S_IWUSR,	\
151e0418088SDavid S. Miller 		show_fan_speed, set_fan_speed, index);			\
152e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO,			\
153e0418088SDavid S. Miller 		show_fan_fault, NULL, index)
154e0418088SDavid S. Miller 
155e0418088SDavid S. Miller fan(0);
156e0418088SDavid S. Miller fan(1);
157e0418088SDavid S. Miller fan(2);
158e0418088SDavid S. Miller fan(3);
159e0418088SDavid S. Miller fan(4);
160e0418088SDavid S. Miller 
161e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6);
162e0418088SDavid S. Miller 
show_temp(struct device * dev,struct device_attribute * attr,char * buf)163a80b10ccSGuenter Roeck static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
164a80b10ccSGuenter Roeck 			 char *buf)
165e0418088SDavid S. Miller {
166e0418088SDavid S. Miller 	int temp_nr = to_sensor_dev_attr(attr)->index;
167e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
168e0418088SDavid S. Miller 	s8 val;
169e0418088SDavid S. Miller 
170e0418088SDavid S. Miller 	val = env_read(p, IREG_LCL_TEMP + temp_nr);
171e0418088SDavid S. Miller 	return sprintf(buf, "%d\n", ((int) val) - 64);
172e0418088SDavid S. Miller }
173e0418088SDavid S. Miller 
174e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(adt7462_local_temp, S_IRUGO, show_temp, NULL, 0);
175e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(cpu0_temp, S_IRUGO, show_temp, NULL, 1);
176e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(cpu1_temp, S_IRUGO, show_temp, NULL, 2);
177e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(motherboard_temp, S_IRUGO, show_temp, NULL, 3);
178e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(lm95221_local_temp, S_IRUGO, show_temp, NULL, 4);
179e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(fire_temp, S_IRUGO, show_temp, NULL, 5);
180e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6);
181e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7);
182e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13);
183e0418088SDavid S. Miller 
show_stat_bit(struct device * dev,struct device_attribute * attr,char * buf)184a80b10ccSGuenter Roeck static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr,
185a80b10ccSGuenter Roeck 			     char *buf)
186e0418088SDavid S. Miller {
187e0418088SDavid S. Miller 	int index = to_sensor_dev_attr(attr)->index;
188e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
189e0418088SDavid S. Miller 	u8 val;
190e0418088SDavid S. Miller 
191e0418088SDavid S. Miller 	val = readb(p->regs + REG_STAT);
192e0418088SDavid S. Miller 	return sprintf(buf, "%d\n", (val & (1 << index)) ? 1 : 0);
193e0418088SDavid S. Miller }
194e0418088SDavid S. Miller 
195e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0);
196e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1);
197e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2);
198a80b10ccSGuenter Roeck static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL,
199a80b10ccSGuenter Roeck 			  3);
200e0418088SDavid S. Miller 
show_fwver(struct device * dev,struct device_attribute * attr,char * buf)201a80b10ccSGuenter Roeck static ssize_t show_fwver(struct device *dev, struct device_attribute *attr,
202a80b10ccSGuenter Roeck 			  char *buf)
203e0418088SDavid S. Miller {
204e0418088SDavid S. Miller 	struct env *p = dev_get_drvdata(dev);
205e0418088SDavid S. Miller 	u8 val;
206e0418088SDavid S. Miller 
207e0418088SDavid S. Miller 	val = readb(p->regs + REG_STAT);
208e0418088SDavid S. Miller 	return sprintf(buf, "%d\n", val >> 4);
209e0418088SDavid S. Miller }
210e0418088SDavid S. Miller 
211e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0);
212e0418088SDavid S. Miller 
show_name(struct device * dev,struct device_attribute * attr,char * buf)213a80b10ccSGuenter Roeck static ssize_t show_name(struct device *dev, struct device_attribute *attr,
214a80b10ccSGuenter Roeck 			 char *buf)
215e0418088SDavid S. Miller {
216e0418088SDavid S. Miller 	return sprintf(buf, "ultra45\n");
217e0418088SDavid S. Miller }
218e0418088SDavid S. Miller 
219e0418088SDavid S. Miller static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
220e0418088SDavid S. Miller 
221e0418088SDavid S. Miller static struct attribute *env_attributes[] = {
222e0418088SDavid S. Miller 	&sensor_dev_attr_fan0_speed.dev_attr.attr,
223e0418088SDavid S. Miller 	&sensor_dev_attr_fan0_fault.dev_attr.attr,
224e0418088SDavid S. Miller 	&sensor_dev_attr_fan1_speed.dev_attr.attr,
225e0418088SDavid S. Miller 	&sensor_dev_attr_fan1_fault.dev_attr.attr,
226e0418088SDavid S. Miller 	&sensor_dev_attr_fan2_speed.dev_attr.attr,
227e0418088SDavid S. Miller 	&sensor_dev_attr_fan2_fault.dev_attr.attr,
228e0418088SDavid S. Miller 	&sensor_dev_attr_fan3_speed.dev_attr.attr,
229e0418088SDavid S. Miller 	&sensor_dev_attr_fan3_fault.dev_attr.attr,
230e0418088SDavid S. Miller 	&sensor_dev_attr_fan4_speed.dev_attr.attr,
231e0418088SDavid S. Miller 	&sensor_dev_attr_fan4_fault.dev_attr.attr,
232e0418088SDavid S. Miller 	&sensor_dev_attr_psu_fan_fault.dev_attr.attr,
233e0418088SDavid S. Miller 	&sensor_dev_attr_adt7462_local_temp.dev_attr.attr,
234e0418088SDavid S. Miller 	&sensor_dev_attr_cpu0_temp.dev_attr.attr,
235e0418088SDavid S. Miller 	&sensor_dev_attr_cpu1_temp.dev_attr.attr,
236e0418088SDavid S. Miller 	&sensor_dev_attr_motherboard_temp.dev_attr.attr,
237e0418088SDavid S. Miller 	&sensor_dev_attr_lm95221_local_temp.dev_attr.attr,
238e0418088SDavid S. Miller 	&sensor_dev_attr_fire_temp.dev_attr.attr,
239e0418088SDavid S. Miller 	&sensor_dev_attr_lsi1064_local_temp.dev_attr.attr,
240e0418088SDavid S. Miller 	&sensor_dev_attr_front_panel_temp.dev_attr.attr,
241e0418088SDavid S. Miller 	&sensor_dev_attr_psu_temp.dev_attr.attr,
242e0418088SDavid S. Miller 	&sensor_dev_attr_fan_failure.dev_attr.attr,
243e0418088SDavid S. Miller 	&sensor_dev_attr_env_bus_busy.dev_attr.attr,
244e0418088SDavid S. Miller 	&sensor_dev_attr_env_data_stale.dev_attr.attr,
245e0418088SDavid S. Miller 	&sensor_dev_attr_tpm_self_test_passed.dev_attr.attr,
246e0418088SDavid S. Miller 	&sensor_dev_attr_firmware_version.dev_attr.attr,
247e0418088SDavid S. Miller 	&sensor_dev_attr_name.dev_attr.attr,
248e0418088SDavid S. Miller 	NULL,
249e0418088SDavid S. Miller };
250e0418088SDavid S. Miller 
251e0418088SDavid S. Miller static const struct attribute_group env_group = {
252e0418088SDavid S. Miller 	.attrs = env_attributes,
253e0418088SDavid S. Miller };
254e0418088SDavid S. Miller 
env_probe(struct platform_device * op)2556c931ae1SBill Pemberton static int env_probe(struct platform_device *op)
256e0418088SDavid S. Miller {
257e8cba3cdSHimangi Saraogi 	struct env *p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
258e0418088SDavid S. Miller 	int err = -ENOMEM;
259e0418088SDavid S. Miller 
260e0418088SDavid S. Miller 	if (!p)
261e0418088SDavid S. Miller 		goto out;
262e0418088SDavid S. Miller 
263e0418088SDavid S. Miller 	spin_lock_init(&p->lock);
264e0418088SDavid S. Miller 
265e0418088SDavid S. Miller 	p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747");
266e0418088SDavid S. Miller 	if (!p->regs)
267e8cba3cdSHimangi Saraogi 		goto out;
268e0418088SDavid S. Miller 
269e0418088SDavid S. Miller 	err = sysfs_create_group(&op->dev.kobj, &env_group);
270e0418088SDavid S. Miller 	if (err)
271e0418088SDavid S. Miller 		goto out_iounmap;
272e0418088SDavid S. Miller 
273e0418088SDavid S. Miller 	p->hwmon_dev = hwmon_device_register(&op->dev);
274e0418088SDavid S. Miller 	if (IS_ERR(p->hwmon_dev)) {
275e0418088SDavid S. Miller 		err = PTR_ERR(p->hwmon_dev);
276e0418088SDavid S. Miller 		goto out_sysfs_remove_group;
277e0418088SDavid S. Miller 	}
278e0418088SDavid S. Miller 
27995de3b25SJean Delvare 	platform_set_drvdata(op, p);
280e0418088SDavid S. Miller 	err = 0;
281e0418088SDavid S. Miller 
282e0418088SDavid S. Miller out:
283e0418088SDavid S. Miller 	return err;
284e0418088SDavid S. Miller 
285e0418088SDavid S. Miller out_sysfs_remove_group:
286e0418088SDavid S. Miller 	sysfs_remove_group(&op->dev.kobj, &env_group);
287e0418088SDavid S. Miller 
288e0418088SDavid S. Miller out_iounmap:
289e0418088SDavid S. Miller 	of_iounmap(&op->resource[0], p->regs, REG_SIZE);
290e0418088SDavid S. Miller 
291e0418088SDavid S. Miller 	goto out;
292e0418088SDavid S. Miller }
293e0418088SDavid S. Miller 
env_remove(struct platform_device * op)294281dfd0bSBill Pemberton static int env_remove(struct platform_device *op)
295e0418088SDavid S. Miller {
29695de3b25SJean Delvare 	struct env *p = platform_get_drvdata(op);
297e0418088SDavid S. Miller 
298e0418088SDavid S. Miller 	if (p) {
299e0418088SDavid S. Miller 		sysfs_remove_group(&op->dev.kobj, &env_group);
300e0418088SDavid S. Miller 		hwmon_device_unregister(p->hwmon_dev);
301e0418088SDavid S. Miller 		of_iounmap(&op->resource[0], p->regs, REG_SIZE);
302e0418088SDavid S. Miller 	}
303e0418088SDavid S. Miller 
304e0418088SDavid S. Miller 	return 0;
305e0418088SDavid S. Miller }
306e0418088SDavid S. Miller 
307fd098316SDavid S. Miller static const struct of_device_id env_match[] = {
308e0418088SDavid S. Miller 	{
309e0418088SDavid S. Miller 		.name = "env-monitor",
310e0418088SDavid S. Miller 		.compatible = "SUNW,ebus-pic16f747-env",
311e0418088SDavid S. Miller 	},
312e0418088SDavid S. Miller 	{},
313e0418088SDavid S. Miller };
314e0418088SDavid S. Miller MODULE_DEVICE_TABLE(of, env_match);
315e0418088SDavid S. Miller 
3164ebb24f7SGrant Likely static struct platform_driver env_driver = {
3174018294bSGrant Likely 	.driver = {
318e0418088SDavid S. Miller 		.name = "ultra45_env",
3194018294bSGrant Likely 		.of_match_table = env_match,
3204018294bSGrant Likely 	},
321e0418088SDavid S. Miller 	.probe		= env_probe,
3229e5e9b7aSBill Pemberton 	.remove		= env_remove,
323e0418088SDavid S. Miller };
324e0418088SDavid S. Miller 
32525a236a5SAxel Lin module_platform_driver(env_driver);
326