Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
e0f8a24e JC |
2 | /* Hwmon client for industrial I/O devices |
3 | * | |
4 | * Copyright (c) 2011 Jonathan Cameron | |
e0f8a24e JC |
5 | */ |
6 | ||
7 | #include <linux/kernel.h> | |
8 | #include <linux/slab.h> | |
b7b568c2 | 9 | #include <linux/mod_devicetable.h> |
e0f8a24e JC |
10 | #include <linux/module.h> |
11 | #include <linux/err.h> | |
12 | #include <linux/platform_device.h> | |
b7b568c2 AS |
13 | #include <linux/property.h> |
14 | ||
e0f8a24e JC |
15 | #include <linux/hwmon.h> |
16 | #include <linux/hwmon-sysfs.h> | |
06458e27 JC |
17 | #include <linux/iio/consumer.h> |
18 | #include <linux/iio/types.h> | |
e0f8a24e JC |
19 | |
20 | /** | |
21 | * struct iio_hwmon_state - device instance state | |
22 | * @channels: filled with array of channels from iio | |
23 | * @num_channels: number of channels in channels (saves counting twice) | |
7f6d70cd GR |
24 | * @attr_group: the group of attributes |
25 | * @groups: null terminated array of attribute groups | |
e0f8a24e JC |
26 | * @attrs: null terminated array of attribute pointers. |
27 | */ | |
28 | struct iio_hwmon_state { | |
29 | struct iio_channel *channels; | |
30 | int num_channels; | |
e0f8a24e | 31 | struct attribute_group attr_group; |
4b49cca3 | 32 | const struct attribute_group *groups[2]; |
e0f8a24e JC |
33 | struct attribute **attrs; |
34 | }; | |
35 | ||
36 | /* | |
37 | * Assumes that IIO and hwmon operate in the same base units. | |
38 | * This is supposed to be true, but needs verification for | |
39 | * new channel types. | |
40 | */ | |
41 | static ssize_t iio_hwmon_read_val(struct device *dev, | |
42 | struct device_attribute *attr, | |
43 | char *buf) | |
44 | { | |
a0e545e0 LPC |
45 | int result; |
46 | int ret; | |
e0f8a24e JC |
47 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
48 | struct iio_hwmon_state *state = dev_get_drvdata(dev); | |
bc34301b MS |
49 | struct iio_channel *chan = &state->channels[sattr->index]; |
50 | enum iio_chan_type type; | |
51 | ||
52 | ret = iio_read_channel_processed(chan, &result); | |
53 | if (ret < 0) | |
54 | return ret; | |
e0f8a24e | 55 | |
bc34301b | 56 | ret = iio_get_channel_type(chan, &type); |
e0f8a24e JC |
57 | if (ret < 0) |
58 | return ret; | |
59 | ||
bc34301b MS |
60 | if (type == IIO_POWER) |
61 | result *= 1000; /* mili-Watts to micro-Watts conversion */ | |
62 | ||
a0e545e0 | 63 | return sprintf(buf, "%d\n", result); |
e0f8a24e JC |
64 | } |
65 | ||
4ae1c61f | 66 | static int iio_hwmon_probe(struct platform_device *pdev) |
e0f8a24e | 67 | { |
c4ac7b98 | 68 | struct device *dev = &pdev->dev; |
e0f8a24e JC |
69 | struct iio_hwmon_state *st; |
70 | struct sensor_device_attribute *a; | |
71 | int ret, i; | |
bc34301b | 72 | int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1, power_i = 1; |
e0f8a24e | 73 | enum iio_chan_type type; |
ca7d98db | 74 | struct iio_channel *channels; |
12005ec3 | 75 | struct device *hwmon_dev; |
b92fe9e3 | 76 | char *sname; |
4b49cca3 | 77 | |
12005ec3 | 78 | channels = devm_iio_channel_get_all(dev); |
9417fefe | 79 | if (IS_ERR(channels)) { |
1c999af5 AS |
80 | ret = PTR_ERR(channels); |
81 | if (ret == -ENODEV) | |
82 | ret = -EPROBE_DEFER; | |
83 | return dev_err_probe(dev, ret, | |
84 | "Failed to get channels\n"); | |
9417fefe | 85 | } |
e0f8a24e | 86 | |
c4ac7b98 | 87 | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); |
12005ec3 MRB |
88 | if (st == NULL) |
89 | return -ENOMEM; | |
e0f8a24e | 90 | |
ca7d98db | 91 | st->channels = channels; |
e0f8a24e JC |
92 | |
93 | /* count how many attributes we have */ | |
94 | while (st->channels[st->num_channels].indio_dev) | |
95 | st->num_channels++; | |
96 | ||
a86854d0 KC |
97 | st->attrs = devm_kcalloc(dev, |
98 | st->num_channels + 1, sizeof(*st->attrs), | |
c4ac7b98 | 99 | GFP_KERNEL); |
12005ec3 MRB |
100 | if (st->attrs == NULL) |
101 | return -ENOMEM; | |
c4ac7b98 | 102 | |
e0f8a24e | 103 | for (i = 0; i < st->num_channels; i++) { |
cb202bb8 AS |
104 | const char *prefix; |
105 | int n; | |
106 | ||
c4ac7b98 | 107 | a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); |
12005ec3 MRB |
108 | if (a == NULL) |
109 | return -ENOMEM; | |
e0f8a24e JC |
110 | |
111 | sysfs_attr_init(&a->dev_attr.attr); | |
314be14b | 112 | ret = iio_get_channel_type(&st->channels[i], &type); |
c4ac7b98 | 113 | if (ret < 0) |
12005ec3 | 114 | return ret; |
c4ac7b98 | 115 | |
e0f8a24e JC |
116 | switch (type) { |
117 | case IIO_VOLTAGE: | |
cb202bb8 AS |
118 | n = in_i++; |
119 | prefix = "in"; | |
e0f8a24e JC |
120 | break; |
121 | case IIO_TEMP: | |
cb202bb8 AS |
122 | n = temp_i++; |
123 | prefix = "temp"; | |
e0f8a24e JC |
124 | break; |
125 | case IIO_CURRENT: | |
cb202bb8 AS |
126 | n = curr_i++; |
127 | prefix = "curr"; | |
e0f8a24e | 128 | break; |
bc34301b MS |
129 | case IIO_POWER: |
130 | n = power_i++; | |
131 | prefix = "power"; | |
132 | break; | |
61bb53bc | 133 | case IIO_HUMIDITYRELATIVE: |
cb202bb8 AS |
134 | n = humidity_i++; |
135 | prefix = "humidity"; | |
61bb53bc | 136 | break; |
e0f8a24e | 137 | default: |
12005ec3 | 138 | return -EINVAL; |
e0f8a24e | 139 | } |
cb202bb8 AS |
140 | |
141 | a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, | |
142 | "%s%d_input", | |
143 | prefix, n); | |
12005ec3 MRB |
144 | if (a->dev_attr.attr.name == NULL) |
145 | return -ENOMEM; | |
146 | ||
e0f8a24e | 147 | a->dev_attr.show = iio_hwmon_read_val; |
389bc38e | 148 | a->dev_attr.attr.mode = 0444; |
e0f8a24e JC |
149 | a->index = i; |
150 | st->attrs[i] = &a->dev_attr.attr; | |
151 | } | |
e0f8a24e | 152 | |
4b49cca3 GR |
153 | st->attr_group.attrs = st->attrs; |
154 | st->groups[0] = &st->attr_group; | |
b92fe9e3 | 155 | |
b7b568c2 AS |
156 | if (dev_fwnode(dev)) { |
157 | sname = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", dev_fwnode(dev)); | |
86103cff GR |
158 | if (!sname) |
159 | return -ENOMEM; | |
160 | strreplace(sname, '-', '_'); | |
161 | } else { | |
162 | sname = "iio_hwmon"; | |
163 | } | |
b92fe9e3 | 164 | |
12005ec3 MRB |
165 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st, |
166 | st->groups); | |
167 | return PTR_ERR_OR_ZERO(hwmon_dev); | |
e0f8a24e JC |
168 | } |
169 | ||
cfe03d64 | 170 | static const struct of_device_id iio_hwmon_of_match[] = { |
a11e619b GR |
171 | { .compatible = "iio-hwmon", }, |
172 | { } | |
173 | }; | |
2ec28196 | 174 | MODULE_DEVICE_TABLE(of, iio_hwmon_of_match); |
a11e619b | 175 | |
561e3121 | 176 | static struct platform_driver iio_hwmon_driver = { |
e0f8a24e JC |
177 | .driver = { |
178 | .name = "iio_hwmon", | |
a11e619b | 179 | .of_match_table = iio_hwmon_of_match, |
e0f8a24e JC |
180 | }, |
181 | .probe = iio_hwmon_probe, | |
e0f8a24e JC |
182 | }; |
183 | ||
d16f6dbd | 184 | module_platform_driver(iio_hwmon_driver); |
e0f8a24e | 185 | |
0f8c9620 | 186 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
e0f8a24e JC |
187 | MODULE_DESCRIPTION("IIO to hwmon driver"); |
188 | MODULE_LICENSE("GPL v2"); |