]>
Commit | Line | Data |
---|---|---|
e0f8a24e JC |
1 | /* Hwmon client for industrial I/O devices |
2 | * | |
3 | * Copyright (c) 2011 Jonathan Cameron | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/hwmon.h> | |
3465a224 | 16 | #include <linux/of.h> |
e0f8a24e | 17 | #include <linux/hwmon-sysfs.h> |
06458e27 JC |
18 | #include <linux/iio/consumer.h> |
19 | #include <linux/iio/types.h> | |
e0f8a24e JC |
20 | |
21 | /** | |
22 | * struct iio_hwmon_state - device instance state | |
23 | * @channels: filled with array of channels from iio | |
24 | * @num_channels: number of channels in channels (saves counting twice) | |
25 | * @hwmon_dev: associated hwmon device | |
26 | * @attr_group: the group of attributes | |
27 | * @attrs: null terminated array of attribute pointers. | |
28 | */ | |
29 | struct iio_hwmon_state { | |
30 | struct iio_channel *channels; | |
31 | int num_channels; | |
32 | struct device *hwmon_dev; | |
33 | struct attribute_group attr_group; | |
4b49cca3 | 34 | const struct attribute_group *groups[2]; |
e0f8a24e JC |
35 | struct attribute **attrs; |
36 | }; | |
37 | ||
38 | /* | |
39 | * Assumes that IIO and hwmon operate in the same base units. | |
40 | * This is supposed to be true, but needs verification for | |
41 | * new channel types. | |
42 | */ | |
43 | static ssize_t iio_hwmon_read_val(struct device *dev, | |
44 | struct device_attribute *attr, | |
45 | char *buf) | |
46 | { | |
a0e545e0 LPC |
47 | int result; |
48 | int ret; | |
e0f8a24e JC |
49 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
50 | struct iio_hwmon_state *state = dev_get_drvdata(dev); | |
51 | ||
a0e545e0 LPC |
52 | ret = iio_read_channel_processed(&state->channels[sattr->index], |
53 | &result); | |
e0f8a24e JC |
54 | if (ret < 0) |
55 | return ret; | |
56 | ||
a0e545e0 | 57 | return sprintf(buf, "%d\n", result); |
e0f8a24e JC |
58 | } |
59 | ||
4ae1c61f | 60 | static int iio_hwmon_probe(struct platform_device *pdev) |
e0f8a24e | 61 | { |
c4ac7b98 | 62 | struct device *dev = &pdev->dev; |
e0f8a24e JC |
63 | struct iio_hwmon_state *st; |
64 | struct sensor_device_attribute *a; | |
65 | int ret, i; | |
61bb53bc | 66 | int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; |
e0f8a24e | 67 | enum iio_chan_type type; |
ca7d98db | 68 | struct iio_channel *channels; |
4b49cca3 | 69 | const char *name = "iio_hwmon"; |
b92fe9e3 | 70 | char *sname; |
4b49cca3 GR |
71 | |
72 | if (dev->of_node && dev->of_node->name) | |
73 | name = dev->of_node->name; | |
ca7d98db GR |
74 | |
75 | channels = iio_channel_get_all(dev); | |
9417fefe QS |
76 | if (IS_ERR(channels)) { |
77 | if (PTR_ERR(channels) == -ENODEV) | |
78 | return -EPROBE_DEFER; | |
ca7d98db | 79 | return PTR_ERR(channels); |
9417fefe | 80 | } |
e0f8a24e | 81 | |
c4ac7b98 | 82 | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); |
4510d198 AL |
83 | if (st == NULL) { |
84 | ret = -ENOMEM; | |
85 | goto error_release_channels; | |
86 | } | |
e0f8a24e | 87 | |
ca7d98db | 88 | st->channels = channels; |
e0f8a24e JC |
89 | |
90 | /* count how many attributes we have */ | |
91 | while (st->channels[st->num_channels].indio_dev) | |
92 | st->num_channels++; | |
93 | ||
c4ac7b98 | 94 | st->attrs = devm_kzalloc(dev, |
4b49cca3 | 95 | sizeof(*st->attrs) * (st->num_channels + 1), |
c4ac7b98 | 96 | GFP_KERNEL); |
e0f8a24e JC |
97 | if (st->attrs == NULL) { |
98 | ret = -ENOMEM; | |
99 | goto error_release_channels; | |
100 | } | |
c4ac7b98 | 101 | |
e0f8a24e | 102 | for (i = 0; i < st->num_channels; i++) { |
c4ac7b98 | 103 | a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); |
e0f8a24e JC |
104 | if (a == NULL) { |
105 | ret = -ENOMEM; | |
c4ac7b98 | 106 | goto error_release_channels; |
e0f8a24e JC |
107 | } |
108 | ||
109 | sysfs_attr_init(&a->dev_attr.attr); | |
314be14b | 110 | ret = iio_get_channel_type(&st->channels[i], &type); |
c4ac7b98 GR |
111 | if (ret < 0) |
112 | goto error_release_channels; | |
113 | ||
e0f8a24e JC |
114 | switch (type) { |
115 | case IIO_VOLTAGE: | |
5d17d3b4 QS |
116 | a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, |
117 | "in%d_input", | |
118 | in_i++); | |
e0f8a24e JC |
119 | break; |
120 | case IIO_TEMP: | |
5d17d3b4 QS |
121 | a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, |
122 | "temp%d_input", | |
123 | temp_i++); | |
e0f8a24e JC |
124 | break; |
125 | case IIO_CURRENT: | |
5d17d3b4 QS |
126 | a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, |
127 | "curr%d_input", | |
128 | curr_i++); | |
e0f8a24e | 129 | break; |
61bb53bc | 130 | case IIO_HUMIDITYRELATIVE: |
5d17d3b4 QS |
131 | a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, |
132 | "humidity%d_input", | |
133 | humidity_i++); | |
61bb53bc | 134 | break; |
e0f8a24e JC |
135 | default: |
136 | ret = -EINVAL; | |
c4ac7b98 | 137 | goto error_release_channels; |
e0f8a24e JC |
138 | } |
139 | if (a->dev_attr.attr.name == NULL) { | |
e0f8a24e | 140 | ret = -ENOMEM; |
c4ac7b98 | 141 | goto error_release_channels; |
e0f8a24e JC |
142 | } |
143 | a->dev_attr.show = iio_hwmon_read_val; | |
144 | a->dev_attr.attr.mode = S_IRUGO; | |
145 | a->index = i; | |
146 | st->attrs[i] = &a->dev_attr.attr; | |
147 | } | |
e0f8a24e | 148 | |
4b49cca3 GR |
149 | st->attr_group.attrs = st->attrs; |
150 | st->groups[0] = &st->attr_group; | |
b92fe9e3 SM |
151 | |
152 | sname = devm_kstrdup(dev, name, GFP_KERNEL); | |
153 | if (!sname) { | |
154 | ret = -ENOMEM; | |
155 | goto error_release_channels; | |
156 | } | |
157 | ||
158 | strreplace(sname, '-', '_'); | |
159 | st->hwmon_dev = hwmon_device_register_with_groups(dev, sname, st, | |
4b49cca3 | 160 | st->groups); |
e0f8a24e JC |
161 | if (IS_ERR(st->hwmon_dev)) { |
162 | ret = PTR_ERR(st->hwmon_dev); | |
4b49cca3 | 163 | goto error_release_channels; |
e0f8a24e | 164 | } |
4b49cca3 | 165 | platform_set_drvdata(pdev, st); |
e0f8a24e JC |
166 | return 0; |
167 | ||
e0f8a24e | 168 | error_release_channels: |
159c8cdd | 169 | iio_channel_release_all(channels); |
e0f8a24e JC |
170 | return ret; |
171 | } | |
172 | ||
447d4f29 | 173 | static int iio_hwmon_remove(struct platform_device *pdev) |
e0f8a24e JC |
174 | { |
175 | struct iio_hwmon_state *st = platform_get_drvdata(pdev); | |
176 | ||
177 | hwmon_device_unregister(st->hwmon_dev); | |
314be14b | 178 | iio_channel_release_all(st->channels); |
e0f8a24e JC |
179 | |
180 | return 0; | |
181 | } | |
182 | ||
cfe03d64 | 183 | static const struct of_device_id iio_hwmon_of_match[] = { |
a11e619b GR |
184 | { .compatible = "iio-hwmon", }, |
185 | { } | |
186 | }; | |
2ec28196 | 187 | MODULE_DEVICE_TABLE(of, iio_hwmon_of_match); |
a11e619b | 188 | |
e0f8a24e JC |
189 | static struct platform_driver __refdata iio_hwmon_driver = { |
190 | .driver = { | |
191 | .name = "iio_hwmon", | |
a11e619b | 192 | .of_match_table = iio_hwmon_of_match, |
e0f8a24e JC |
193 | }, |
194 | .probe = iio_hwmon_probe, | |
e543acf0 | 195 | .remove = iio_hwmon_remove, |
e0f8a24e JC |
196 | }; |
197 | ||
d16f6dbd | 198 | module_platform_driver(iio_hwmon_driver); |
e0f8a24e | 199 | |
0f8c9620 | 200 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
e0f8a24e JC |
201 | MODULE_DESCRIPTION("IIO to hwmon driver"); |
202 | MODULE_LICENSE("GPL v2"); |