Blame view

drivers/hwmon/ibmpowernv.c 16.8 KB
d5bb994bc   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
2
3
4
  /*
   * IBM PowerNV platform sensors for temperature/fan/voltage/power
   * Copyright (C) 2014 IBM
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   */
  
  #define DRVNAME		"ibmpowernv"
  #define pr_fmt(fmt)	DRVNAME ": " fmt
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/of.h>
  #include <linux/slab.h>
  
  #include <linux/platform_device.h>
  #include <asm/opal.h>
  #include <linux/err.h>
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
21
  #include <asm/cputhreads.h>
8416915c1   Guenter Roeck   hwmon: (ibmpowern...
22
  #include <asm/smp.h>
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
23
24
  
  #define MAX_ATTR_LEN	32
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
25
  #define MAX_LABEL_LEN	64
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
26
27
28
29
30
31
32
33
34
35
36
37
  
  /* Sensor suffix name from DT */
  #define DT_FAULT_ATTR_SUFFIX		"faulted"
  #define DT_DATA_ATTR_SUFFIX		"data"
  #define DT_THRESHOLD_ATTR_SUFFIX	"thrs"
  
  /*
   * Enumerates all the types of sensors in the POWERNV platform and does index
   * into 'struct sensor_group'
   */
  enum sensors {
  	FAN,
96124610e   Cédric Le Goater   hwmon: (ibmpowern...
38
  	TEMP,
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
39
40
  	POWER_SUPPLY,
  	POWER_INPUT,
3a2b3d37e   Shilpasri G Bhat   hwmon: (ibmpowern...
41
  	CURRENT,
43d2974b6   Shilpasri G Bhat   hwmon: (ibmpowern...
42
  	ENERGY,
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
43
44
  	MAX_SENSOR_TYPE,
  };
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
45
  #define INVALID_INDEX (-1U)
3ab521601   Cédric Le Goater   hwmon: (ibmpowern...
46
47
48
49
50
51
52
53
54
55
  /*
   * 'compatible' string properties for sensor types as defined in old
   * PowerNV firmware (skiboot). These are ordered as 'enum sensors'.
   */
  static const char * const legacy_compatibles[] = {
  	"ibm,opal-sensor-cooling-fan",
  	"ibm,opal-sensor-amb-temp",
  	"ibm,opal-sensor-power-supply",
  	"ibm,opal-sensor-power"
  };
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
56
  static struct sensor_group {
3ab521601   Cédric Le Goater   hwmon: (ibmpowern...
57
  	const char *name; /* matches property 'sensor-type' */
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
58
59
  	struct attribute_group group;
  	u32 attr_count;
fcaf57b67   Cédric Le Goater   hwmon: (ibmpowern...
60
  	u32 hwmon_index;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
61
  } sensor_groups[] = {
3ab521601   Cédric Le Goater   hwmon: (ibmpowern...
62
63
64
  	{ "fan"   },
  	{ "temp"  },
  	{ "in"    },
3a2b3d37e   Shilpasri G Bhat   hwmon: (ibmpowern...
65
66
  	{ "power" },
  	{ "curr"  },
43d2974b6   Shilpasri G Bhat   hwmon: (ibmpowern...
67
  	{ "energy" },
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
68
69
70
71
  };
  
  struct sensor_data {
  	u32 id; /* An opaque id of the firmware for each sensor */
fcaf57b67   Cédric Le Goater   hwmon: (ibmpowern...
72
73
  	u32 hwmon_index;
  	u32 opal_index;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
74
  	enum sensors type;
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
75
  	char label[MAX_LABEL_LEN];
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
76
77
  	char name[MAX_ATTR_LEN];
  	struct device_attribute dev_attr;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
78
79
80
81
82
83
84
  	struct sensor_group_data *sgrp_data;
  };
  
  struct sensor_group_data {
  	struct mutex mutex;
  	u32 gid;
  	bool enable;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
85
86
87
88
  };
  
  struct platform_data {
  	const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1];
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
89
  	struct sensor_group_data *sgrp_data;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
90
  	u32 sensors_count; /* Total count of sensors from each group */
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
91
  	u32 nr_sensor_groups; /* Total number of sensor groups */
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
92
  };
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
93
94
95
96
97
98
  static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
  			   char *buf)
  {
  	struct sensor_data *sdata = container_of(devattr, struct sensor_data,
  						 dev_attr);
  	ssize_t ret;
3c8c049aa   Shilpasri G Bhat   hwmon: (ibmpowern...
99
  	u64 x;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
100
101
  	if (sdata->sgrp_data && !sdata->sgrp_data->enable)
  		return -ENODATA;
3c8c049aa   Shilpasri G Bhat   hwmon: (ibmpowern...
102
  	ret =  opal_get_sensor_data_u64(sdata->id, &x);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
103

24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
104
105
106
107
  	if (ret)
  		return ret;
  
  	/* Convert temperature to milli-degrees */
96124610e   Cédric Le Goater   hwmon: (ibmpowern...
108
  	if (sdata->type == TEMP)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
109
110
111
112
  		x *= 1000;
  	/* Convert power to micro-watts */
  	else if (sdata->type == POWER_INPUT)
  		x *= 1000000;
3c8c049aa   Shilpasri G Bhat   hwmon: (ibmpowern...
113
114
  	return sprintf(buf, "%llu
  ", x);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
115
  }
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  static ssize_t show_enable(struct device *dev,
  			   struct device_attribute *devattr, char *buf)
  {
  	struct sensor_data *sdata = container_of(devattr, struct sensor_data,
  						 dev_attr);
  
  	return sprintf(buf, "%u
  ", sdata->sgrp_data->enable);
  }
  
  static ssize_t store_enable(struct device *dev,
  			    struct device_attribute *devattr,
  			    const char *buf, size_t count)
  {
  	struct sensor_data *sdata = container_of(devattr, struct sensor_data,
  						 dev_attr);
  	struct sensor_group_data *sgrp_data = sdata->sgrp_data;
  	int ret;
  	bool data;
  
  	ret = kstrtobool(buf, &data);
  	if (ret)
  		return ret;
  
  	ret = mutex_lock_interruptible(&sgrp_data->mutex);
  	if (ret)
  		return ret;
  
  	if (data != sgrp_data->enable) {
  		ret =  sensor_group_enable(sgrp_data->gid, data);
  		if (!ret)
  			sgrp_data->enable = data;
  	}
  
  	if (!ret)
  		ret = count;
  
  	mutex_unlock(&sgrp_data->mutex);
  	return ret;
  }
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
156
157
158
159
160
161
162
163
164
  static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
  			  char *buf)
  {
  	struct sensor_data *sdata = container_of(devattr, struct sensor_data,
  						 dev_attr);
  
  	return sprintf(buf, "%s
  ", sdata->label);
  }
e3e61f01d   Geert Uytterhoeven   hwmon: (ibmpowern...
165
  static int get_logical_cpu(int hwcpu)
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
166
167
168
169
170
171
172
173
174
  {
  	int cpu;
  
  	for_each_possible_cpu(cpu)
  		if (get_hard_smp_processor_id(cpu) == hwcpu)
  			return cpu;
  
  	return -ENOENT;
  }
e3e61f01d   Geert Uytterhoeven   hwmon: (ibmpowern...
175
176
  static void make_sensor_label(struct device_node *np,
  			      struct sensor_data *sdata, const char *label)
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
177
  {
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
178
  	u32 id;
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
179
  	size_t n;
6a096871b   Takashi Iwai   hwmon: (ibmpowern...
180
  	n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label);
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
181
182
183
184
185
186
187
188
189
190
  
  	/*
  	 * Core temp pretty print
  	 */
  	if (!of_property_read_u32(np, "ibm,pir", &id)) {
  		int cpuid = get_logical_cpu(id);
  
  		if (cpuid >= 0)
  			/*
  			 * The digital thermal sensors are associated
acf32964d   Michael Neuling   hwmon: (ibmpowern...
191
  			 * with a core.
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
192
  			 */
6a096871b   Takashi Iwai   hwmon: (ibmpowern...
193
  			n += scnprintf(sdata->label + n,
acf32964d   Michael Neuling   hwmon: (ibmpowern...
194
195
  				      sizeof(sdata->label) - n, " %d",
  				      cpuid);
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
196
  		else
6a096871b   Takashi Iwai   hwmon: (ibmpowern...
197
  			n += scnprintf(sdata->label + n,
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
198
199
200
201
202
203
204
  				      sizeof(sdata->label) - n, " phy%d", id);
  	}
  
  	/*
  	 * Membuffer pretty print
  	 */
  	if (!of_property_read_u32(np, "ibm,chip-id", &id))
6a096871b   Takashi Iwai   hwmon: (ibmpowern...
205
  		n += scnprintf(sdata->label + n, sizeof(sdata->label) - n,
3df2f59f0   Cédric Le Goater   hwmon: (ibmpowern...
206
  			      " %d", id & 0xffff);
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
207
208
209
  }
  
  static int get_sensor_index_attr(const char *name, u32 *index, char *attr)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  {
  	char *hash_pos = strchr(name, '#');
  	char buf[8] = { 0 };
  	char *dash_pos;
  	u32 copy_len;
  	int err;
  
  	if (!hash_pos)
  		return -EINVAL;
  
  	dash_pos = strchr(hash_pos, '-');
  	if (!dash_pos)
  		return -EINVAL;
  
  	copy_len = dash_pos - hash_pos - 1;
  	if (copy_len >= sizeof(buf))
  		return -EINVAL;
  
  	strncpy(buf, hash_pos + 1, copy_len);
  
  	err = kstrtou32(buf, 10, index);
  	if (err)
  		return err;
  
  	strncpy(attr, dash_pos + 1, MAX_ATTR_LEN);
  
  	return 0;
  }
ccc9ac6cc   Cédric Le Goater   hwmon: (ibmpowern...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  static const char *convert_opal_attr_name(enum sensors type,
  					  const char *opal_attr)
  {
  	const char *attr_name = NULL;
  
  	if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) {
  		attr_name = "fault";
  	} else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) {
  		attr_name = "input";
  	} else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) {
  		if (type == TEMP)
  			attr_name = "max";
  		else if (type == FAN)
  			attr_name = "min";
  	}
  
  	return attr_name;
  }
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
256
257
258
259
260
261
  /*
   * This function translates the DT node name into the 'hwmon' attribute name.
   * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.
   * which need to be mapped as fan2_input, temp1_max respectively before
   * populating them inside hwmon device class.
   */
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
262
263
  static const char *parse_opal_node_name(const char *node_name,
  					enum sensors type, u32 *index)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
264
265
  {
  	char attr_suffix[MAX_ATTR_LEN];
ccc9ac6cc   Cédric Le Goater   hwmon: (ibmpowern...
266
  	const char *attr_name;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
267
  	int err;
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
268
269
270
  	err = get_sensor_index_attr(node_name, index, attr_suffix);
  	if (err)
  		return ERR_PTR(err);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
271

ccc9ac6cc   Cédric Le Goater   hwmon: (ibmpowern...
272
273
  	attr_name = convert_opal_attr_name(type, attr_suffix);
  	if (!attr_name)
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
274
  		return ERR_PTR(-ENOENT);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
275

f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
276
  	return attr_name;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
277
  }
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
278
279
280
  static int get_sensor_type(struct device_node *np)
  {
  	enum sensors type;
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
281
  	const char *str;
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
282

3ab521601   Cédric Le Goater   hwmon: (ibmpowern...
283
284
  	for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) {
  		if (of_device_is_compatible(np, legacy_compatibles[type]))
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
285
286
  			return type;
  	}
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
287
288
289
290
291
292
293
294
295
296
297
298
299
  
  	/*
  	 * Let's check if we have a newer device tree
  	 */
  	if (!of_device_is_compatible(np, "ibm,opal-sensor"))
  		return MAX_SENSOR_TYPE;
  
  	if (of_property_read_string(np, "sensor-type", &str))
  		return MAX_SENSOR_TYPE;
  
  	for (type = 0; type < MAX_SENSOR_TYPE; type++)
  		if (!strcmp(str, sensor_groups[type].name))
  			return type;
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
300
301
  	return MAX_SENSOR_TYPE;
  }
fcaf57b67   Cédric Le Goater   hwmon: (ibmpowern...
302
303
304
305
  static u32 get_sensor_hwmon_index(struct sensor_data *sdata,
  				  struct sensor_data *sdata_table, int count)
  {
  	int i;
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
306
307
308
309
310
311
312
313
314
  	/*
  	 * We don't use the OPAL index on newer device trees
  	 */
  	if (sdata->opal_index != INVALID_INDEX) {
  		for (i = 0; i < count; i++)
  			if (sdata_table[i].opal_index == sdata->opal_index &&
  			    sdata_table[i].type == sdata->type)
  				return sdata_table[i].hwmon_index;
  	}
fcaf57b67   Cédric Le Goater   hwmon: (ibmpowern...
315
316
  	return ++sensor_groups[sdata->type].hwmon_index;
  }
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  static int init_sensor_group_data(struct platform_device *pdev,
  				  struct platform_data *pdata)
  {
  	struct sensor_group_data *sgrp_data;
  	struct device_node *groups, *sgrp;
  	int count = 0, ret = 0;
  	enum sensors type;
  
  	groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
  	if (!groups)
  		return ret;
  
  	for_each_child_of_node(groups, sgrp) {
  		type = get_sensor_type(sgrp);
  		if (type != MAX_SENSOR_TYPE)
  			pdata->nr_sensor_groups++;
  	}
  
  	if (!pdata->nr_sensor_groups)
  		goto out;
  
  	sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups,
  				 sizeof(*sgrp_data), GFP_KERNEL);
  	if (!sgrp_data) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	for_each_child_of_node(groups, sgrp) {
  		u32 gid;
  
  		type = get_sensor_type(sgrp);
  		if (type == MAX_SENSOR_TYPE)
  			continue;
  
  		if (of_property_read_u32(sgrp, "sensor-group-id", &gid))
  			continue;
  
  		if (of_count_phandle_with_args(sgrp, "sensors", NULL) <= 0)
  			continue;
  
  		sensor_groups[type].attr_count++;
  		sgrp_data[count].gid = gid;
  		mutex_init(&sgrp_data[count].mutex);
  		sgrp_data[count++].enable = false;
  	}
  
  	pdata->sgrp_data = sgrp_data;
  out:
  	of_node_put(groups);
  	return ret;
  }
  
  static struct sensor_group_data *get_sensor_group(struct platform_data *pdata,
  						  struct device_node *node,
  						  enum sensors gtype)
  {
  	struct sensor_group_data *sgrp_data = pdata->sgrp_data;
  	struct device_node *groups, *sgrp;
  
  	groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
  	if (!groups)
  		return NULL;
  
  	for_each_child_of_node(groups, sgrp) {
  		struct of_phandle_iterator it;
  		u32 gid;
  		int rc, i;
  		enum sensors type;
  
  		type = get_sensor_type(sgrp);
  		if (type != gtype)
  			continue;
  
  		if (of_property_read_u32(sgrp, "sensor-group-id", &gid))
  			continue;
  
  		of_for_each_phandle(&it, rc, sgrp, "sensors", NULL, 0)
  			if (it.phandle == node->phandle) {
  				of_node_put(it.node);
  				break;
  			}
  
  		if (rc)
  			continue;
  
  		for (i = 0; i < pdata->nr_sensor_groups; i++)
  			if (gid == sgrp_data[i].gid) {
  				of_node_put(sgrp);
  				of_node_put(groups);
  				return &sgrp_data[i];
  			}
  	}
  
  	of_node_put(groups);
  	return NULL;
  }
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
414
  static int populate_attr_groups(struct platform_device *pdev)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
415
416
417
418
419
  {
  	struct platform_data *pdata = platform_get_drvdata(pdev);
  	const struct attribute_group **pgroups = pdata->attr_groups;
  	struct device_node *opal, *np;
  	enum sensors type;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
420
421
422
423
424
  	int ret;
  
  	ret = init_sensor_group_data(pdev, pdata);
  	if (ret)
  		return ret;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
425
426
  
  	opal = of_find_node_by_path("/ibm,opal/sensors");
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
427
  	for_each_child_of_node(opal, np) {
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
428
  		const char *label;
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
429
  		type = get_sensor_type(np);
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
430
431
432
433
434
435
  		if (type == MAX_SENSOR_TYPE)
  			continue;
  
  		sensor_groups[type].attr_count++;
  
  		/*
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
436
  		 * add attributes for labels, min and max
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
437
438
  		 */
  		if (!of_property_read_string(np, "label", &label))
c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
439
  			sensor_groups[type].attr_count++;
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
440
441
442
443
  		if (of_find_property(np, "sensor-data-min", NULL))
  			sensor_groups[type].attr_count++;
  		if (of_find_property(np, "sensor-data-max", NULL))
  			sensor_groups[type].attr_count++;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
444
445
446
447
448
  	}
  
  	of_node_put(opal);
  
  	for (type = 0; type < MAX_SENSOR_TYPE; type++) {
a86854d0c   Kees Cook   treewide: devm_kz...
449
450
451
  		sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev,
  					sensor_groups[type].attr_count + 1,
  					sizeof(struct attribute *),
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
452
453
454
455
456
457
458
459
460
461
462
  					GFP_KERNEL);
  		if (!sensor_groups[type].group.attrs)
  			return -ENOMEM;
  
  		pgroups[type] = &sensor_groups[type].group;
  		pdata->sensors_count += sensor_groups[type].attr_count;
  		sensor_groups[type].attr_count = 0;
  	}
  
  	return 0;
  }
9e4f74b11   Cédric Le Goater   hwmon: (ibmpowern...
463
464
465
  static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,
  			      ssize_t (*show)(struct device *dev,
  					      struct device_attribute *attr,
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
466
467
468
469
  					      char *buf),
  			    ssize_t (*store)(struct device *dev,
  					     struct device_attribute *attr,
  					     const char *buf, size_t count))
9e4f74b11   Cédric Le Goater   hwmon: (ibmpowern...
470
471
472
473
474
475
476
  {
  	snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s",
  		 sensor_groups[sdata->type].name, sdata->hwmon_index,
  		 attr_name);
  
  	sysfs_attr_init(&sdata->dev_attr.attr);
  	sdata->dev_attr.attr.name = sdata->name;
9e4f74b11   Cédric Le Goater   hwmon: (ibmpowern...
477
  	sdata->dev_attr.show = show;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
478
479
480
481
482
483
  	if (store) {
  		sdata->dev_attr.store = store;
  		sdata->dev_attr.attr.mode = 0664;
  	} else {
  		sdata->dev_attr.attr.mode = 0444;
  	}
9e4f74b11   Cédric Le Goater   hwmon: (ibmpowern...
484
  }
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
485
486
487
  static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid,
  			    const char *attr_name, enum sensors type,
  			    const struct attribute_group *pgroup,
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
488
  			    struct sensor_group_data *sgrp_data,
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
489
490
  			    ssize_t (*show)(struct device *dev,
  					    struct device_attribute *attr,
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
491
492
493
494
  					    char *buf),
  			    ssize_t (*store)(struct device *dev,
  					     struct device_attribute *attr,
  					     const char *buf, size_t count))
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
495
496
497
498
499
  {
  	sdata->id = sid;
  	sdata->type = type;
  	sdata->opal_index = od;
  	sdata->hwmon_index = hd;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
500
  	create_hwmon_attr(sdata, attr_name, show, store);
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
501
  	pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
502
  	sdata->sgrp_data = sgrp_data;
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  }
  
  static char *get_max_attr(enum sensors type)
  {
  	switch (type) {
  	case POWER_INPUT:
  		return "input_highest";
  	default:
  		return "highest";
  	}
  }
  
  static char *get_min_attr(enum sensors type)
  {
  	switch (type) {
  	case POWER_INPUT:
  		return "input_lowest";
  	default:
  		return "lowest";
  	}
  }
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
524
525
526
527
528
529
  /*
   * Iterate through the device tree for each child of 'sensors' node, create
   * a sysfs attribute file, the file is named by translating the DT node name
   * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
   * etc..
   */
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
530
  static int create_device_attrs(struct platform_device *pdev)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
531
532
533
534
535
  {
  	struct platform_data *pdata = platform_get_drvdata(pdev);
  	const struct attribute_group **pgroups = pdata->attr_groups;
  	struct device_node *opal, *np;
  	struct sensor_data *sdata;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
536
  	u32 count = 0;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
537
  	u32 group_attr_id[MAX_SENSOR_TYPE] = {0};
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
538

a86854d0c   Kees Cook   treewide: devm_kz...
539
540
  	sdata = devm_kcalloc(&pdev->dev,
  			     pdata->sensors_count, sizeof(*sdata),
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
541
  			     GFP_KERNEL);
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
542
543
  	if (!sdata)
  		return -ENOMEM;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
544

e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
545
  	opal = of_find_node_by_path("/ibm,opal/sensors");
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
546
  	for_each_child_of_node(opal, np) {
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
547
  		struct sensor_group_data *sgrp_data;
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
548
  		const char *attr_name;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
549
550
  		u32 opal_index, hw_id;
  		u32 sensor_id;
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
551
  		const char *label;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
552
  		enum sensors type;
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
553

c4ad47206   Cédric Le Goater   hwmon: (ibmpowern...
554
  		type = get_sensor_type(np);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
555
556
  		if (type == MAX_SENSOR_TYPE)
  			continue;
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
557
558
559
560
561
562
  		/*
  		 * Newer device trees use a "sensor-data" property
  		 * name for input.
  		 */
  		if (of_property_read_u32(np, "sensor-id", &sensor_id) &&
  		    of_property_read_u32(np, "sensor-data", &sensor_id)) {
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
563
  			dev_info(&pdev->dev,
0debe4d0b   Rob Herring   hwmon: Convert to...
564
565
566
  				 "'sensor-id' missing in the node '%pOFn'
  ",
  				 np);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
567
568
  			continue;
  		}
18d03f3cb   Axel Lin   hwmon: (ibmpowern...
569
  		sdata[count].id = sensor_id;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
570
  		sdata[count].type = type;
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
571

14681637a   Cédric Le Goater   hwmon: (ibmpowern...
572
573
574
575
576
577
  		/*
  		 * If we can not parse the node name, it means we are
  		 * running on a newer device tree. We can just forget
  		 * about the OPAL index and use a defaut value for the
  		 * hwmon attribute name
  		 */
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
578
579
  		attr_name = parse_opal_node_name(np->name, type, &opal_index);
  		if (IS_ERR(attr_name)) {
14681637a   Cédric Le Goater   hwmon: (ibmpowern...
580
581
  			attr_name = "input";
  			opal_index = INVALID_INDEX;
f9f54f16b   Cédric Le Goater   hwmon: (ibmpowern...
582
  		}
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
583
584
585
586
587
588
  		hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count);
  		sgrp_data = get_sensor_group(pdata, np, type);
  		populate_sensor(&sdata[count], opal_index, hw_id, sensor_id,
  				attr_name, type, pgroups[type], sgrp_data,
  				show_sensor, NULL);
  		count++;
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
589
590
591
592
593
594
595
596
  
  		if (!of_property_read_string(np, "label", &label)) {
  			/*
  			 * For the label attribute, we can reuse the
  			 * "properties" of the previous "input"
  			 * attribute. They are related to the same
  			 * sensor.
  			 */
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
597
598
  
  			make_sensor_label(np, &sdata[count], label);
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
599
  			populate_sensor(&sdata[count], opal_index, hw_id,
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
600
  					sensor_id, "label", type, pgroups[type],
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
601
  					NULL, show_label, NULL);
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
602
603
  			count++;
  		}
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
604

996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
605
606
  		if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) {
  			attr_name = get_max_attr(type);
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
607
  			populate_sensor(&sdata[count], opal_index, hw_id,
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
608
  					sensor_id, attr_name, type,
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
609
610
  					pgroups[type], sgrp_data, show_sensor,
  					NULL);
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
611
612
  			count++;
  		}
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
613

996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
614
615
  		if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) {
  			attr_name = get_min_attr(type);
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
616
  			populate_sensor(&sdata[count], opal_index, hw_id,
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
617
  					sensor_id, attr_name, type,
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
618
619
620
621
622
623
624
625
626
627
628
629
  					pgroups[type], sgrp_data, show_sensor,
  					NULL);
  			count++;
  		}
  
  		if (sgrp_data && !sgrp_data->enable) {
  			sgrp_data->enable = true;
  			hw_id = ++group_attr_id[type];
  			populate_sensor(&sdata[count], opal_index, hw_id,
  					sgrp_data->gid, "enable", type,
  					pgroups[type], sgrp_data, show_enable,
  					store_enable);
996cf5a5e   Shilpasri G Bhat   hwmon: (ibmpowern...
630
  			count++;
2bcd3787b   Cédric Le Goater   hwmon: (ibmpowern...
631
  		}
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
632
  	}
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
633
  	of_node_put(opal);
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
634
  	return 0;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
635
  }
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
636
  static int ibmpowernv_probe(struct platform_device *pdev)
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
637
638
639
640
641
642
643
644
645
646
647
  {
  	struct platform_data *pdata;
  	struct device *hwmon_dev;
  	int err;
  
  	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
  	if (!pdata)
  		return -ENOMEM;
  
  	platform_set_drvdata(pdev, pdata);
  	pdata->sensors_count = 0;
e0da99123   Shilpasri G Bhat   hwmon: (ibmpowern...
648
  	pdata->nr_sensor_groups = 0;
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
  	err = populate_attr_groups(pdev);
  	if (err)
  		return err;
  
  	/* Create sysfs attribute data for each sensor found in the DT */
  	err = create_device_attrs(pdev);
  	if (err)
  		return err;
  
  	/* Finally, register with hwmon */
  	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
  							   pdata,
  							   pdata->attr_groups);
  
  	return PTR_ERR_OR_ZERO(hwmon_dev);
  }
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
665
666
667
668
669
670
671
  static const struct platform_device_id opal_sensor_driver_ids[] = {
  	{
  		.name = "opal-sensor",
  	},
  	{ }
  };
  MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
0b056b29f   Cédric Le Goater   hwmon: (ibmpowern...
672
673
674
675
676
  static const struct of_device_id opal_sensor_match[] = {
  	{ .compatible	= "ibm,opal-sensor" },
  	{ },
  };
  MODULE_DEVICE_TABLE(of, opal_sensor_match);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
677
  static struct platform_driver ibmpowernv_driver = {
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
678
679
680
  	.probe		= ibmpowernv_probe,
  	.id_table	= opal_sensor_driver_ids,
  	.driver		= {
8de303bae   Neelesh Gupta   hwmon: (ibmpowern...
681
  		.name	= DRVNAME,
0b056b29f   Cédric Le Goater   hwmon: (ibmpowern...
682
  		.of_match_table	= opal_sensor_match,
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
683
684
  	},
  };
3bdec670d   Axel Lin   hwmon: (ibmpowern...
685
  module_platform_driver(ibmpowernv_driver);
24c1aa858   Neelesh Gupta   hwmon: (powerpc/p...
686
687
688
689
  
  MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
  MODULE_DESCRIPTION("IBM POWERNV platform sensors");
  MODULE_LICENSE("GPL");