diff options
-rw-r--r-- | Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt | 3 | ||||
-rw-r--r-- | Documentation/hwmon/k10temp | 2 | ||||
-rw-r--r-- | Documentation/hwmon/menf21bmc | 50 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 15 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ab8500.c | 5 | ||||
-rw-r--r-- | drivers/hwmon/ads1015.c | 21 | ||||
-rw-r--r-- | drivers/hwmon/da9052-hwmon.c | 54 | ||||
-rw-r--r-- | drivers/hwmon/da9055-hwmon.c | 52 | ||||
-rw-r--r-- | drivers/hwmon/k10temp.c | 157 | ||||
-rw-r--r-- | drivers/hwmon/menf21bmc_hwmon.c | 230 | ||||
-rw-r--r-- | drivers/hwmon/ntc_thermistor.c | 25 | ||||
-rw-r--r-- | drivers/hwmon/smsc47b397.c | 51 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 9 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-menf21bmc.c | 131 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 15 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/menf21bmc.c | 132 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 10 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/menf21bmc_wdt.c | 203 |
23 files changed, 949 insertions, 228 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt b/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt index 2391e5c41999..fcca8e744f41 100644 --- a/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt +++ b/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt | |||
@@ -25,6 +25,9 @@ Requires node properties: | |||
25 | - "io-channels" Channel node of ADC to be used for | 25 | - "io-channels" Channel node of ADC to be used for |
26 | conversion. | 26 | conversion. |
27 | 27 | ||
28 | Optional node properties: | ||
29 | - "#thermal-sensor-cells" Used to expose itself to thermal fw. | ||
30 | |||
28 | Read more about iio bindings at | 31 | Read more about iio bindings at |
29 | Documentation/devicetree/bindings/iio/iio-bindings.txt | 32 | Documentation/devicetree/bindings/iio/iio-bindings.txt |
30 | 33 | ||
diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp index ee6d30ec1522..254d2f55345a 100644 --- a/Documentation/hwmon/k10temp +++ b/Documentation/hwmon/k10temp | |||
@@ -11,7 +11,7 @@ Supported chips: | |||
11 | Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra) | 11 | Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra) |
12 | * AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series) | 12 | * AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series) |
13 | * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series) | 13 | * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series) |
14 | * AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri" | 14 | * AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri", "Carrizo" |
15 | * AMD Family 16h processors: "Kabini", "Mullins" | 15 | * AMD Family 16h processors: "Kabini", "Mullins" |
16 | 16 | ||
17 | Prefix: 'k10temp' | 17 | Prefix: 'k10temp' |
diff --git a/Documentation/hwmon/menf21bmc b/Documentation/hwmon/menf21bmc new file mode 100644 index 000000000000..2a273a065c5e --- /dev/null +++ b/Documentation/hwmon/menf21bmc | |||
@@ -0,0 +1,50 @@ | |||
1 | Kernel driver menf21bmc_hwmon | ||
2 | ============================= | ||
3 | |||
4 | Supported chips: | ||
5 | * MEN 14F021P00 | ||
6 | Prefix: 'menf21bmc_hwmon' | ||
7 | Adresses scanned: - | ||
8 | |||
9 | Author: Andreas Werner <andreas.werner@men.de> | ||
10 | |||
11 | Description | ||
12 | ----------- | ||
13 | |||
14 | The menf21bmc is a Board Management Controller (BMC) which provides an I2C | ||
15 | interface to the host to access the features implemented in the BMC. | ||
16 | |||
17 | This driver gives access to the voltage monitoring feature of the main | ||
18 | voltages of the board. | ||
19 | The voltage sensors are connected to the ADC inputs of the BMC which is | ||
20 | a PIC16F917 Mikrocontroller. | ||
21 | |||
22 | Usage Notes | ||
23 | ----------- | ||
24 | |||
25 | This driver is part of the MFD driver named "menf21bmc" and does | ||
26 | not auto-detect devices. | ||
27 | You will have to instantiate the MFD driver explicitly. | ||
28 | Please see Documentation/i2c/instantiating-devices for | ||
29 | details. | ||
30 | |||
31 | Sysfs entries | ||
32 | ------------- | ||
33 | |||
34 | The following attributes are supported. All attributes are read only | ||
35 | The Limits are read once by the driver. | ||
36 | |||
37 | in0_input +3.3V input voltage | ||
38 | in1_input +5.0V input voltage | ||
39 | in2_input +12.0V input voltage | ||
40 | in3_input +5V Standby input voltage | ||
41 | in4_input VBAT (on board battery) | ||
42 | |||
43 | in[0-4]_min Minimum voltage limit | ||
44 | in[0-4]_max Maximum voltage limit | ||
45 | |||
46 | in0_label "MON_3_3V" | ||
47 | in1_label "MON_5V" | ||
48 | in2_label "MON_12V" | ||
49 | in3_label "5V_STANDBY" | ||
50 | in4_label "VBAT" | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 40d4796886c9..8b0902106cc0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -7364,6 +7364,14 @@ T: git git://linuxtv.org/media_tree.git | |||
7364 | S: Maintained | 7364 | S: Maintained |
7365 | F: drivers/media/usb/pwc/* | 7365 | F: drivers/media/usb/pwc/* |
7366 | 7366 | ||
7367 | PWM FAN DRIVER | ||
7368 | M: Kamil Debski <k.debski@samsung.com> | ||
7369 | L: lm-sensors@lm-sensors.org | ||
7370 | S: Supported | ||
7371 | F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt | ||
7372 | F: Documentation/hwmon/pwm-fan | ||
7373 | F: drivers/hwmon/pwm-fan.c | ||
7374 | |||
7367 | PWM SUBSYSTEM | 7375 | PWM SUBSYSTEM |
7368 | M: Thierry Reding <thierry.reding@gmail.com> | 7376 | M: Thierry Reding <thierry.reding@gmail.com> |
7369 | L: linux-pwm@vger.kernel.org | 7377 | L: linux-pwm@vger.kernel.org |
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f00d048aa583..5286d7ce1f9e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -280,8 +280,8 @@ config SENSORS_K10TEMP | |||
280 | If you say yes here you get support for the temperature | 280 | If you say yes here you get support for the temperature |
281 | sensor(s) inside your CPU. Supported are later revisions of | 281 | sensor(s) inside your CPU. Supported are later revisions of |
282 | the AMD Family 10h and all revisions of the AMD Family 11h, | 282 | the AMD Family 10h and all revisions of the AMD Family 11h, |
283 | 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri) and | 283 | 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri/Carrizo) |
284 | 16h (Kabini/Mullins) microarchitectures. | 284 | and 16h (Kabini/Mullins) microarchitectures. |
285 | 285 | ||
286 | This driver can also be built as a module. If so, the module | 286 | This driver can also be built as a module. If so, the module |
287 | will be called k10temp. | 287 | will be called k10temp. |
@@ -839,6 +839,16 @@ config SENSORS_MCP3021 | |||
839 | This driver can also be built as a module. If so, the module | 839 | This driver can also be built as a module. If so, the module |
840 | will be called mcp3021. | 840 | will be called mcp3021. |
841 | 841 | ||
842 | config SENSORS_MENF21BMC_HWMON | ||
843 | tristate "MEN 14F021P00 BMC Hardware Monitoring" | ||
844 | depends on MFD_MENF21BMC | ||
845 | help | ||
846 | Say Y here to include support for the MEN 14F021P00 BMC | ||
847 | hardware monitoring. | ||
848 | |||
849 | This driver can also be built as a module. If so the module | ||
850 | will be called menf21bmc_hwmon. | ||
851 | |||
842 | config SENSORS_ADCXX | 852 | config SENSORS_ADCXX |
843 | tristate "National Semiconductor ADCxxxSxxx" | 853 | tristate "National Semiconductor ADCxxxSxxx" |
844 | depends on SPI_MASTER | 854 | depends on SPI_MASTER |
@@ -1077,6 +1087,7 @@ config SENSORS_PC87427 | |||
1077 | config SENSORS_NTC_THERMISTOR | 1087 | config SENSORS_NTC_THERMISTOR |
1078 | tristate "NTC thermistor support from Murata" | 1088 | tristate "NTC thermistor support from Murata" |
1079 | depends on !OF || IIO=n || IIO | 1089 | depends on !OF || IIO=n || IIO |
1090 | depends on THERMAL || !THERMAL_OF | ||
1080 | help | 1091 | help |
1081 | This driver supports NTC thermistors sensor reading and its | 1092 | This driver supports NTC thermistors sensor reading and its |
1082 | interpretation. The driver can also monitor the temperature and | 1093 | interpretation. The driver can also monitor the temperature and |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index be28152c9848..c90a7611efaa 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o | |||
115 | obj-$(CONFIG_SENSORS_MAX6697) += max6697.o | 115 | obj-$(CONFIG_SENSORS_MAX6697) += max6697.o |
116 | obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o | 116 | obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o |
117 | obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o | 117 | obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o |
118 | obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o | ||
118 | obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o | 119 | obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o |
119 | obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o | 120 | obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o |
120 | obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o | 121 | obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o |
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c index d844dc806853..8b6a4f4c8774 100644 --- a/drivers/hwmon/ab8500.c +++ b/drivers/hwmon/ab8500.c | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | * When the AB8500 thermal warning temperature is reached (threshold cannot | 7 | * When the AB8500 thermal warning temperature is reached (threshold cannot |
8 | * be changed by SW), an interrupt is set, and if no further action is taken | 8 | * be changed by SW), an interrupt is set, and if no further action is taken |
9 | * within a certain time frame, pm_power off will be called. | 9 | * within a certain time frame, kernel_power_off will be called. |
10 | * | 10 | * |
11 | * When AB8500 thermal shutdown temperature is reached a hardware shutdown of | 11 | * When AB8500 thermal shutdown temperature is reached a hardware shutdown of |
12 | * the AB8500 will occur. | 12 | * the AB8500 will occur. |
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/power/ab8500.h> | 23 | #include <linux/power/ab8500.h> |
24 | #include <linux/reboot.h> | ||
24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
25 | #include <linux/sysfs.h> | 26 | #include <linux/sysfs.h> |
26 | #include "abx500.h" | 27 | #include "abx500.h" |
@@ -106,7 +107,7 @@ static void ab8500_thermal_power_off(struct work_struct *work) | |||
106 | 107 | ||
107 | dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); | 108 | dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); |
108 | 109 | ||
109 | pm_power_off(); | 110 | kernel_power_off(); |
110 | } | 111 | } |
111 | 112 | ||
112 | static ssize_t ab8500_show_name(struct device *dev, | 113 | static ssize_t ab8500_show_name(struct device *dev, |
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index 126516414c11..f155b8380481 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c | |||
@@ -184,20 +184,18 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) | |||
184 | return -EINVAL; | 184 | return -EINVAL; |
185 | 185 | ||
186 | for_each_child_of_node(client->dev.of_node, node) { | 186 | for_each_child_of_node(client->dev.of_node, node) { |
187 | const __be32 *property; | 187 | u32 pval; |
188 | int len; | ||
189 | unsigned int channel; | 188 | unsigned int channel; |
190 | unsigned int pga = ADS1015_DEFAULT_PGA; | 189 | unsigned int pga = ADS1015_DEFAULT_PGA; |
191 | unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; | 190 | unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; |
192 | 191 | ||
193 | property = of_get_property(node, "reg", &len); | 192 | if (of_property_read_u32(node, "reg", &pval)) { |
194 | if (!property || len != sizeof(int)) { | ||
195 | dev_err(&client->dev, "invalid reg on %s\n", | 193 | dev_err(&client->dev, "invalid reg on %s\n", |
196 | node->full_name); | 194 | node->full_name); |
197 | continue; | 195 | continue; |
198 | } | 196 | } |
199 | 197 | ||
200 | channel = be32_to_cpup(property); | 198 | channel = pval; |
201 | if (channel >= ADS1015_CHANNELS) { | 199 | if (channel >= ADS1015_CHANNELS) { |
202 | dev_err(&client->dev, | 200 | dev_err(&client->dev, |
203 | "invalid channel index %d on %s\n", | 201 | "invalid channel index %d on %s\n", |
@@ -205,20 +203,17 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) | |||
205 | continue; | 203 | continue; |
206 | } | 204 | } |
207 | 205 | ||
208 | property = of_get_property(node, "ti,gain", &len); | 206 | if (!of_property_read_u32(node, "ti,gain", &pval)) { |
209 | if (property && len == sizeof(int)) { | 207 | pga = pval; |
210 | pga = be32_to_cpup(property); | ||
211 | if (pga > 6) { | 208 | if (pga > 6) { |
212 | dev_err(&client->dev, | 209 | dev_err(&client->dev, "invalid gain on %s\n", |
213 | "invalid gain on %s\n", | ||
214 | node->full_name); | 210 | node->full_name); |
215 | return -EINVAL; | 211 | return -EINVAL; |
216 | } | 212 | } |
217 | } | 213 | } |
218 | 214 | ||
219 | property = of_get_property(node, "ti,datarate", &len); | 215 | if (!of_property_read_u32(node, "ti,datarate", &pval)) { |
220 | if (property && len == sizeof(int)) { | 216 | data_rate = pval; |
221 | data_rate = be32_to_cpup(property); | ||
222 | if (data_rate > 7) { | 217 | if (data_rate > 7) { |
223 | dev_err(&client->dev, | 218 | dev_err(&client->dev, |
224 | "invalid data_rate on %s\n", | 219 | "invalid data_rate on %s\n", |
diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index d14ab3c45daa..692b3f34d88c 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c | |||
@@ -26,7 +26,6 @@ | |||
26 | 26 | ||
27 | struct da9052_hwmon { | 27 | struct da9052_hwmon { |
28 | struct da9052 *da9052; | 28 | struct da9052 *da9052; |
29 | struct device *class_device; | ||
30 | struct mutex hwmon_lock; | 29 | struct mutex hwmon_lock; |
31 | }; | 30 | }; |
32 | 31 | ||
@@ -190,13 +189,6 @@ static ssize_t da9052_read_vbbat(struct device *dev, | |||
190 | return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret)); | 189 | return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret)); |
191 | } | 190 | } |
192 | 191 | ||
193 | static ssize_t da9052_hwmon_show_name(struct device *dev, | ||
194 | struct device_attribute *devattr, | ||
195 | char *buf) | ||
196 | { | ||
197 | return sprintf(buf, "da9052\n"); | ||
198 | } | ||
199 | |||
200 | static ssize_t show_label(struct device *dev, | 192 | static ssize_t show_label(struct device *dev, |
201 | struct device_attribute *devattr, char *buf) | 193 | struct device_attribute *devattr, char *buf) |
202 | { | 194 | { |
@@ -243,10 +235,7 @@ static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL, | |||
243 | static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL, | 235 | static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL, |
244 | DA9052_ADC_TJUNC); | 236 | DA9052_ADC_TJUNC); |
245 | 237 | ||
246 | static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL); | 238 | static struct attribute *da9052_attrs[] = { |
247 | |||
248 | static struct attribute *da9052_attr[] = { | ||
249 | &dev_attr_name.attr, | ||
250 | &sensor_dev_attr_in0_input.dev_attr.attr, | 239 | &sensor_dev_attr_in0_input.dev_attr.attr, |
251 | &sensor_dev_attr_in0_label.dev_attr.attr, | 240 | &sensor_dev_attr_in0_label.dev_attr.attr, |
252 | &sensor_dev_attr_in3_input.dev_attr.attr, | 241 | &sensor_dev_attr_in3_input.dev_attr.attr, |
@@ -268,54 +257,29 @@ static struct attribute *da9052_attr[] = { | |||
268 | NULL | 257 | NULL |
269 | }; | 258 | }; |
270 | 259 | ||
271 | static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr}; | 260 | ATTRIBUTE_GROUPS(da9052); |
272 | 261 | ||
273 | static int da9052_hwmon_probe(struct platform_device *pdev) | 262 | static int da9052_hwmon_probe(struct platform_device *pdev) |
274 | { | 263 | { |
264 | struct device *dev = &pdev->dev; | ||
275 | struct da9052_hwmon *hwmon; | 265 | struct da9052_hwmon *hwmon; |
276 | int ret; | 266 | struct device *hwmon_dev; |
277 | 267 | ||
278 | hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon), | 268 | hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL); |
279 | GFP_KERNEL); | ||
280 | if (!hwmon) | 269 | if (!hwmon) |
281 | return -ENOMEM; | 270 | return -ENOMEM; |
282 | 271 | ||
283 | mutex_init(&hwmon->hwmon_lock); | 272 | mutex_init(&hwmon->hwmon_lock); |
284 | hwmon->da9052 = dev_get_drvdata(pdev->dev.parent); | 273 | hwmon->da9052 = dev_get_drvdata(pdev->dev.parent); |
285 | 274 | ||
286 | platform_set_drvdata(pdev, hwmon); | 275 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052", |
287 | 276 | hwmon, | |
288 | ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group); | 277 | da9052_groups); |
289 | if (ret) | 278 | return PTR_ERR_OR_ZERO(hwmon_dev); |
290 | goto err_mem; | ||
291 | |||
292 | hwmon->class_device = hwmon_device_register(&pdev->dev); | ||
293 | if (IS_ERR(hwmon->class_device)) { | ||
294 | ret = PTR_ERR(hwmon->class_device); | ||
295 | goto err_sysfs; | ||
296 | } | ||
297 | |||
298 | return 0; | ||
299 | |||
300 | err_sysfs: | ||
301 | sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group); | ||
302 | err_mem: | ||
303 | return ret; | ||
304 | } | ||
305 | |||
306 | static int da9052_hwmon_remove(struct platform_device *pdev) | ||
307 | { | ||
308 | struct da9052_hwmon *hwmon = platform_get_drvdata(pdev); | ||
309 | |||
310 | hwmon_device_unregister(hwmon->class_device); | ||
311 | sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group); | ||
312 | |||
313 | return 0; | ||
314 | } | 279 | } |
315 | 280 | ||
316 | static struct platform_driver da9052_hwmon_driver = { | 281 | static struct platform_driver da9052_hwmon_driver = { |
317 | .probe = da9052_hwmon_probe, | 282 | .probe = da9052_hwmon_probe, |
318 | .remove = da9052_hwmon_remove, | ||
319 | .driver = { | 283 | .driver = { |
320 | .name = "da9052-hwmon", | 284 | .name = "da9052-hwmon", |
321 | .owner = THIS_MODULE, | 285 | .owner = THIS_MODULE, |
diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c index 35eb7738d711..9916a3fb4bb9 100644 --- a/drivers/hwmon/da9055-hwmon.c +++ b/drivers/hwmon/da9055-hwmon.c | |||
@@ -36,7 +36,6 @@ | |||
36 | 36 | ||
37 | struct da9055_hwmon { | 37 | struct da9055_hwmon { |
38 | struct da9055 *da9055; | 38 | struct da9055 *da9055; |
39 | struct device *class_device; | ||
40 | struct mutex hwmon_lock; | 39 | struct mutex hwmon_lock; |
41 | struct mutex irq_lock; | 40 | struct mutex irq_lock; |
42 | struct completion done; | 41 | struct completion done; |
@@ -200,13 +199,6 @@ static ssize_t da9055_read_tjunc(struct device *dev, | |||
200 | + 3076332, 10000)); | 199 | + 3076332, 10000)); |
201 | } | 200 | } |
202 | 201 | ||
203 | static ssize_t da9055_hwmon_show_name(struct device *dev, | ||
204 | struct device_attribute *devattr, | ||
205 | char *buf) | ||
206 | { | ||
207 | return sprintf(buf, "da9055\n"); | ||
208 | } | ||
209 | |||
210 | static ssize_t show_label(struct device *dev, | 202 | static ssize_t show_label(struct device *dev, |
211 | struct device_attribute *devattr, char *buf) | 203 | struct device_attribute *devattr, char *buf) |
212 | { | 204 | { |
@@ -236,10 +228,7 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, da9055_read_tjunc, NULL, | |||
236 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, | 228 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, |
237 | DA9055_ADC_TJUNC); | 229 | DA9055_ADC_TJUNC); |
238 | 230 | ||
239 | static DEVICE_ATTR(name, S_IRUGO, da9055_hwmon_show_name, NULL); | 231 | static struct attribute *da9055_attrs[] = { |
240 | |||
241 | static struct attribute *da9055_attr[] = { | ||
242 | &dev_attr_name.attr, | ||
243 | &sensor_dev_attr_in0_input.dev_attr.attr, | 232 | &sensor_dev_attr_in0_input.dev_attr.attr, |
244 | &sensor_dev_attr_in0_label.dev_attr.attr, | 233 | &sensor_dev_attr_in0_label.dev_attr.attr, |
245 | &sensor_dev_attr_in1_input.dev_attr.attr, | 234 | &sensor_dev_attr_in1_input.dev_attr.attr, |
@@ -254,15 +243,16 @@ static struct attribute *da9055_attr[] = { | |||
254 | NULL | 243 | NULL |
255 | }; | 244 | }; |
256 | 245 | ||
257 | static const struct attribute_group da9055_attr_group = {.attrs = da9055_attr}; | 246 | ATTRIBUTE_GROUPS(da9055); |
258 | 247 | ||
259 | static int da9055_hwmon_probe(struct platform_device *pdev) | 248 | static int da9055_hwmon_probe(struct platform_device *pdev) |
260 | { | 249 | { |
250 | struct device *dev = &pdev->dev; | ||
261 | struct da9055_hwmon *hwmon; | 251 | struct da9055_hwmon *hwmon; |
252 | struct device *hwmon_dev; | ||
262 | int hwmon_irq, ret; | 253 | int hwmon_irq, ret; |
263 | 254 | ||
264 | hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9055_hwmon), | 255 | hwmon = devm_kzalloc(dev, sizeof(struct da9055_hwmon), GFP_KERNEL); |
265 | GFP_KERNEL); | ||
266 | if (!hwmon) | 256 | if (!hwmon) |
267 | return -ENOMEM; | 257 | return -ENOMEM; |
268 | 258 | ||
@@ -272,8 +262,6 @@ static int da9055_hwmon_probe(struct platform_device *pdev) | |||
272 | init_completion(&hwmon->done); | 262 | init_completion(&hwmon->done); |
273 | hwmon->da9055 = dev_get_drvdata(pdev->dev.parent); | 263 | hwmon->da9055 = dev_get_drvdata(pdev->dev.parent); |
274 | 264 | ||
275 | platform_set_drvdata(pdev, hwmon); | ||
276 | |||
277 | hwmon_irq = platform_get_irq_byname(pdev, "HWMON"); | 265 | hwmon_irq = platform_get_irq_byname(pdev, "HWMON"); |
278 | if (hwmon_irq < 0) | 266 | if (hwmon_irq < 0) |
279 | return hwmon_irq; | 267 | return hwmon_irq; |
@@ -288,36 +276,14 @@ static int da9055_hwmon_probe(struct platform_device *pdev) | |||
288 | return ret; | 276 | return ret; |
289 | } | 277 | } |
290 | 278 | ||
291 | ret = sysfs_create_group(&pdev->dev.kobj, &da9055_attr_group); | 279 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055", |
292 | if (ret) | 280 | hwmon, |
293 | return ret; | 281 | da9055_groups); |
294 | 282 | return PTR_ERR_OR_ZERO(hwmon_dev); | |
295 | hwmon->class_device = hwmon_device_register(&pdev->dev); | ||
296 | if (IS_ERR(hwmon->class_device)) { | ||
297 | ret = PTR_ERR(hwmon->class_device); | ||
298 | goto err; | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | |||
303 | err: | ||
304 | sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); | ||
305 | return ret; | ||
306 | } | ||
307 | |||
308 | static int da9055_hwmon_remove(struct platform_device *pdev) | ||
309 | { | ||
310 | struct da9055_hwmon *hwmon = platform_get_drvdata(pdev); | ||
311 | |||
312 | sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); | ||
313 | hwmon_device_unregister(hwmon->class_device); | ||
314 | |||
315 | return 0; | ||
316 | } | 283 | } |
317 | 284 | ||
318 | static struct platform_driver da9055_hwmon_driver = { | 285 | static struct platform_driver da9055_hwmon_driver = { |
319 | .probe = da9055_hwmon_probe, | 286 | .probe = da9055_hwmon_probe, |
320 | .remove = da9055_hwmon_remove, | ||
321 | .driver = { | 287 | .driver = { |
322 | .name = "da9055-hwmon", | 288 | .name = "da9055-hwmon", |
323 | .owner = THIS_MODULE, | 289 | .owner = THIS_MODULE, |
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index f7b46f68ef43..1e7bdcdcb295 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c | |||
@@ -33,6 +33,9 @@ static bool force; | |||
33 | module_param(force, bool, 0444); | 33 | module_param(force, bool, 0444); |
34 | MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); | 34 | MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); |
35 | 35 | ||
36 | /* Provide lock for writing to NB_SMU_IND_ADDR */ | ||
37 | static DEFINE_MUTEX(nb_smu_ind_mutex); | ||
38 | |||
36 | /* CPUID function 0x80000001, ebx */ | 39 | /* CPUID function 0x80000001, ebx */ |
37 | #define CPUID_PKGTYPE_MASK 0xf0000000 | 40 | #define CPUID_PKGTYPE_MASK 0xf0000000 |
38 | #define CPUID_PKGTYPE_F 0x00000000 | 41 | #define CPUID_PKGTYPE_F 0x00000000 |
@@ -51,13 +54,38 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); | |||
51 | #define REG_NORTHBRIDGE_CAPABILITIES 0xe8 | 54 | #define REG_NORTHBRIDGE_CAPABILITIES 0xe8 |
52 | #define NB_CAP_HTC 0x00000400 | 55 | #define NB_CAP_HTC 0x00000400 |
53 | 56 | ||
57 | /* | ||
58 | * For F15h M60h, functionality of REG_REPORTED_TEMPERATURE | ||
59 | * has been moved to D0F0xBC_xD820_0CA4 [Reported Temperature | ||
60 | * Control] | ||
61 | */ | ||
62 | #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 | ||
63 | #define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573 | ||
64 | |||
65 | static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn, | ||
66 | int offset, u32 *val) | ||
67 | { | ||
68 | mutex_lock(&nb_smu_ind_mutex); | ||
69 | pci_bus_write_config_dword(pdev->bus, devfn, | ||
70 | 0xb8, offset); | ||
71 | pci_bus_read_config_dword(pdev->bus, devfn, | ||
72 | 0xbc, val); | ||
73 | mutex_unlock(&nb_smu_ind_mutex); | ||
74 | } | ||
75 | |||
54 | static ssize_t show_temp(struct device *dev, | 76 | static ssize_t show_temp(struct device *dev, |
55 | struct device_attribute *attr, char *buf) | 77 | struct device_attribute *attr, char *buf) |
56 | { | 78 | { |
57 | u32 regval; | 79 | u32 regval; |
58 | 80 | struct pci_dev *pdev = dev_get_drvdata(dev); | |
59 | pci_read_config_dword(to_pci_dev(dev), | 81 | |
60 | REG_REPORTED_TEMPERATURE, ®val); | 82 | if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model == 0x60) { |
83 | amd_nb_smu_index_read(pdev, PCI_DEVFN(0, 0), | ||
84 | F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, | ||
85 | ®val); | ||
86 | } else { | ||
87 | pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, ®val); | ||
88 | } | ||
61 | return sprintf(buf, "%u\n", (regval >> 21) * 125); | 89 | return sprintf(buf, "%u\n", (regval >> 21) * 125); |
62 | } | 90 | } |
63 | 91 | ||
@@ -75,7 +103,7 @@ static ssize_t show_temp_crit(struct device *dev, | |||
75 | u32 regval; | 103 | u32 regval; |
76 | int value; | 104 | int value; |
77 | 105 | ||
78 | pci_read_config_dword(to_pci_dev(dev), | 106 | pci_read_config_dword(dev_get_drvdata(dev), |
79 | REG_HARDWARE_THERMAL_CONTROL, ®val); | 107 | REG_HARDWARE_THERMAL_CONTROL, ®val); |
80 | value = ((regval >> 16) & 0x7f) * 500 + 52000; | 108 | value = ((regval >> 16) & 0x7f) * 500 + 52000; |
81 | if (show_hyst) | 109 | if (show_hyst) |
@@ -83,17 +111,43 @@ static ssize_t show_temp_crit(struct device *dev, | |||
83 | return sprintf(buf, "%d\n", value); | 111 | return sprintf(buf, "%d\n", value); |
84 | } | 112 | } |
85 | 113 | ||
86 | static ssize_t show_name(struct device *dev, | ||
87 | struct device_attribute *attr, char *buf) | ||
88 | { | ||
89 | return sprintf(buf, "k10temp\n"); | ||
90 | } | ||
91 | |||
92 | static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); | 114 | static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); |
93 | static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL); | 115 | static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL); |
94 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); | 116 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); |
95 | static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1); | 117 | static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1); |
96 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | 118 | |
119 | static umode_t k10temp_is_visible(struct kobject *kobj, | ||
120 | struct attribute *attr, int index) | ||
121 | { | ||
122 | struct device *dev = container_of(kobj, struct device, kobj); | ||
123 | struct pci_dev *pdev = dev_get_drvdata(dev); | ||
124 | |||
125 | if (index >= 2) { | ||
126 | u32 reg_caps, reg_htc; | ||
127 | |||
128 | pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, | ||
129 | ®_caps); | ||
130 | pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, | ||
131 | ®_htc); | ||
132 | if (!(reg_caps & NB_CAP_HTC) || !(reg_htc & HTC_ENABLE)) | ||
133 | return 0; | ||
134 | } | ||
135 | return attr->mode; | ||
136 | } | ||
137 | |||
138 | static struct attribute *k10temp_attrs[] = { | ||
139 | &dev_attr_temp1_input.attr, | ||
140 | &dev_attr_temp1_max.attr, | ||
141 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | ||
142 | &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, | ||
143 | NULL | ||
144 | }; | ||
145 | |||
146 | static const struct attribute_group k10temp_group = { | ||
147 | .attrs = k10temp_attrs, | ||
148 | .is_visible = k10temp_is_visible, | ||
149 | }; | ||
150 | __ATTRIBUTE_GROUPS(k10temp); | ||
97 | 151 | ||
98 | static bool has_erratum_319(struct pci_dev *pdev) | 152 | static bool has_erratum_319(struct pci_dev *pdev) |
99 | { | 153 | { |
@@ -132,76 +186,23 @@ static bool has_erratum_319(struct pci_dev *pdev) | |||
132 | static int k10temp_probe(struct pci_dev *pdev, | 186 | static int k10temp_probe(struct pci_dev *pdev, |
133 | const struct pci_device_id *id) | 187 | const struct pci_device_id *id) |
134 | { | 188 | { |
135 | struct device *hwmon_dev; | ||
136 | u32 reg_caps, reg_htc; | ||
137 | int unreliable = has_erratum_319(pdev); | 189 | int unreliable = has_erratum_319(pdev); |
138 | int err; | 190 | struct device *dev = &pdev->dev; |
139 | 191 | struct device *hwmon_dev; | |
140 | if (unreliable && !force) { | ||
141 | dev_err(&pdev->dev, | ||
142 | "unreliable CPU thermal sensor; monitoring disabled\n"); | ||
143 | err = -ENODEV; | ||
144 | goto exit; | ||
145 | } | ||
146 | |||
147 | err = device_create_file(&pdev->dev, &dev_attr_temp1_input); | ||
148 | if (err) | ||
149 | goto exit; | ||
150 | err = device_create_file(&pdev->dev, &dev_attr_temp1_max); | ||
151 | if (err) | ||
152 | goto exit_remove; | ||
153 | |||
154 | pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, ®_caps); | ||
155 | pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, ®_htc); | ||
156 | if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) { | ||
157 | err = device_create_file(&pdev->dev, | ||
158 | &sensor_dev_attr_temp1_crit.dev_attr); | ||
159 | if (err) | ||
160 | goto exit_remove; | ||
161 | err = device_create_file(&pdev->dev, | ||
162 | &sensor_dev_attr_temp1_crit_hyst.dev_attr); | ||
163 | if (err) | ||
164 | goto exit_remove; | ||
165 | } | ||
166 | |||
167 | err = device_create_file(&pdev->dev, &dev_attr_name); | ||
168 | if (err) | ||
169 | goto exit_remove; | ||
170 | |||
171 | hwmon_dev = hwmon_device_register(&pdev->dev); | ||
172 | if (IS_ERR(hwmon_dev)) { | ||
173 | err = PTR_ERR(hwmon_dev); | ||
174 | goto exit_remove; | ||
175 | } | ||
176 | pci_set_drvdata(pdev, hwmon_dev); | ||
177 | 192 | ||
178 | if (unreliable && force) | 193 | if (unreliable) { |
179 | dev_warn(&pdev->dev, | 194 | if (!force) { |
195 | dev_err(dev, | ||
196 | "unreliable CPU thermal sensor; monitoring disabled\n"); | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | dev_warn(dev, | ||
180 | "unreliable CPU thermal sensor; check erratum 319\n"); | 200 | "unreliable CPU thermal sensor; check erratum 319\n"); |
181 | return 0; | 201 | } |
182 | |||
183 | exit_remove: | ||
184 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
185 | device_remove_file(&pdev->dev, &dev_attr_temp1_input); | ||
186 | device_remove_file(&pdev->dev, &dev_attr_temp1_max); | ||
187 | device_remove_file(&pdev->dev, | ||
188 | &sensor_dev_attr_temp1_crit.dev_attr); | ||
189 | device_remove_file(&pdev->dev, | ||
190 | &sensor_dev_attr_temp1_crit_hyst.dev_attr); | ||
191 | exit: | ||
192 | return err; | ||
193 | } | ||
194 | 202 | ||
195 | static void k10temp_remove(struct pci_dev *pdev) | 203 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", pdev, |
196 | { | 204 | k10temp_groups); |
197 | hwmon_device_unregister(pci_get_drvdata(pdev)); | 205 | return PTR_ERR_OR_ZERO(hwmon_dev); |
198 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
199 | device_remove_file(&pdev->dev, &dev_attr_temp1_input); | ||
200 | device_remove_file(&pdev->dev, &dev_attr_temp1_max); | ||
201 | device_remove_file(&pdev->dev, | ||
202 | &sensor_dev_attr_temp1_crit.dev_attr); | ||
203 | device_remove_file(&pdev->dev, | ||
204 | &sensor_dev_attr_temp1_crit_hyst.dev_attr); | ||
205 | } | 206 | } |
206 | 207 | ||
207 | static const struct pci_device_id k10temp_id_table[] = { | 208 | static const struct pci_device_id k10temp_id_table[] = { |
@@ -211,6 +212,7 @@ static const struct pci_device_id k10temp_id_table[] = { | |||
211 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, | 212 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, |
212 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, | 213 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, |
213 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, | 214 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, |
215 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, | ||
214 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, | 216 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, |
215 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, | 217 | { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, |
216 | {} | 218 | {} |
@@ -221,7 +223,6 @@ static struct pci_driver k10temp_driver = { | |||
221 | .name = "k10temp", | 223 | .name = "k10temp", |
222 | .id_table = k10temp_id_table, | 224 | .id_table = k10temp_id_table, |
223 | .probe = k10temp_probe, | 225 | .probe = k10temp_probe, |
224 | .remove = k10temp_remove, | ||
225 | }; | 226 | }; |
226 | 227 | ||
227 | module_pci_driver(k10temp_driver); | 228 | module_pci_driver(k10temp_driver); |
diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c new file mode 100644 index 000000000000..c92229d321c9 --- /dev/null +++ b/drivers/hwmon/menf21bmc_hwmon.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * MEN 14F021P00 Board Management Controller (BMC) hwmon driver. | ||
3 | * | ||
4 | * This is the core hwmon driver of the MEN 14F021P00 BMC. | ||
5 | * The BMC monitors the board voltages which can be access with this | ||
6 | * driver through sysfs. | ||
7 | * | ||
8 | * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/hwmon.h> | ||
20 | #include <linux/hwmon-sysfs.h> | ||
21 | #include <linux/jiffies.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/i2c.h> | ||
24 | |||
25 | #define DRV_NAME "menf21bmc_hwmon" | ||
26 | |||
27 | #define BMC_VOLT_COUNT 5 | ||
28 | #define MENF21BMC_V33 0 | ||
29 | #define MENF21BMC_V5 1 | ||
30 | #define MENF21BMC_V12 2 | ||
31 | #define MENF21BMC_V5_SB 3 | ||
32 | #define MENF21BMC_VBAT 4 | ||
33 | |||
34 | #define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx) | ||
35 | #define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx) | ||
36 | #define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx) | ||
37 | |||
38 | struct menf21bmc_hwmon { | ||
39 | bool valid; | ||
40 | struct i2c_client *i2c_client; | ||
41 | unsigned long last_update; | ||
42 | int in_val[BMC_VOLT_COUNT]; | ||
43 | int in_min[BMC_VOLT_COUNT]; | ||
44 | int in_max[BMC_VOLT_COUNT]; | ||
45 | }; | ||
46 | |||
47 | static const char *const input_names[] = { | ||
48 | [MENF21BMC_V33] = "MON_3_3V", | ||
49 | [MENF21BMC_V5] = "MON_5V", | ||
50 | [MENF21BMC_V12] = "MON_12V", | ||
51 | [MENF21BMC_V5_SB] = "5V_STANDBY", | ||
52 | [MENF21BMC_VBAT] = "VBAT" | ||
53 | }; | ||
54 | |||
55 | static struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev) | ||
56 | { | ||
57 | int i; | ||
58 | int val; | ||
59 | struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); | ||
60 | struct menf21bmc_hwmon *data_ret = drv_data; | ||
61 | |||
62 | if (time_after(jiffies, drv_data->last_update + HZ) | ||
63 | || !drv_data->valid) { | ||
64 | for (i = 0; i < BMC_VOLT_COUNT; i++) { | ||
65 | val = i2c_smbus_read_word_data(drv_data->i2c_client, | ||
66 | IDX_TO_VOLT_INP_CMD(i)); | ||
67 | if (val < 0) { | ||
68 | data_ret = ERR_PTR(val); | ||
69 | goto abort; | ||
70 | } | ||
71 | drv_data->in_val[i] = val; | ||
72 | } | ||
73 | drv_data->last_update = jiffies; | ||
74 | drv_data->valid = true; | ||
75 | } | ||
76 | abort: | ||
77 | return data_ret; | ||
78 | } | ||
79 | |||
80 | static int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data) | ||
81 | { | ||
82 | int i, val; | ||
83 | |||
84 | for (i = 0; i < BMC_VOLT_COUNT; i++) { | ||
85 | val = i2c_smbus_read_word_data(drv_data->i2c_client, | ||
86 | IDX_TO_VOLT_MIN_CMD(i)); | ||
87 | if (val < 0) | ||
88 | return val; | ||
89 | |||
90 | drv_data->in_min[i] = val; | ||
91 | |||
92 | val = i2c_smbus_read_word_data(drv_data->i2c_client, | ||
93 | IDX_TO_VOLT_MAX_CMD(i)); | ||
94 | if (val < 0) | ||
95 | return val; | ||
96 | |||
97 | drv_data->in_max[i] = val; | ||
98 | } | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static ssize_t | ||
103 | show_label(struct device *dev, struct device_attribute *devattr, char *buf) | ||
104 | { | ||
105 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
106 | |||
107 | return sprintf(buf, "%s\n", input_names[attr->index]); | ||
108 | } | ||
109 | |||
110 | static ssize_t | ||
111 | show_in(struct device *dev, struct device_attribute *devattr, char *buf) | ||
112 | { | ||
113 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
114 | struct menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev); | ||
115 | |||
116 | if (IS_ERR(drv_data)) | ||
117 | return PTR_ERR(drv_data); | ||
118 | |||
119 | return sprintf(buf, "%d\n", drv_data->in_val[attr->index]); | ||
120 | } | ||
121 | |||
122 | static ssize_t | ||
123 | show_min(struct device *dev, struct device_attribute *devattr, char *buf) | ||
124 | { | ||
125 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
126 | struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); | ||
127 | |||
128 | return sprintf(buf, "%d\n", drv_data->in_min[attr->index]); | ||
129 | } | ||
130 | |||
131 | static ssize_t | ||
132 | show_max(struct device *dev, struct device_attribute *devattr, char *buf) | ||
133 | { | ||
134 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
135 | struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev); | ||
136 | |||
137 | return sprintf(buf, "%d\n", drv_data->in_max[attr->index]); | ||
138 | } | ||
139 | |||
140 | #define create_voltage_sysfs(idx) \ | ||
141 | static SENSOR_DEVICE_ATTR(in##idx##_input, S_IRUGO, \ | ||
142 | show_in, NULL, idx); \ | ||
143 | static SENSOR_DEVICE_ATTR(in##idx##_min, S_IRUGO, \ | ||
144 | show_min, NULL, idx); \ | ||
145 | static SENSOR_DEVICE_ATTR(in##idx##_max, S_IRUGO, \ | ||
146 | show_max, NULL, idx); \ | ||
147 | static SENSOR_DEVICE_ATTR(in##idx##_label, S_IRUGO, \ | ||
148 | show_label, NULL, idx); | ||
149 | |||
150 | create_voltage_sysfs(0); | ||
151 | create_voltage_sysfs(1); | ||
152 | create_voltage_sysfs(2); | ||
153 | create_voltage_sysfs(3); | ||
154 | create_voltage_sysfs(4); | ||
155 | |||
156 | static struct attribute *menf21bmc_hwmon_attrs[] = { | ||
157 | &sensor_dev_attr_in0_input.dev_attr.attr, | ||
158 | &sensor_dev_attr_in0_min.dev_attr.attr, | ||
159 | &sensor_dev_attr_in0_max.dev_attr.attr, | ||
160 | &sensor_dev_attr_in0_label.dev_attr.attr, | ||
161 | |||
162 | &sensor_dev_attr_in1_input.dev_attr.attr, | ||
163 | &sensor_dev_attr_in1_min.dev_attr.attr, | ||
164 | &sensor_dev_attr_in1_max.dev_attr.attr, | ||
165 | &sensor_dev_attr_in1_label.dev_attr.attr, | ||
166 | |||
167 | &sensor_dev_attr_in2_input.dev_attr.attr, | ||
168 | &sensor_dev_attr_in2_min.dev_attr.attr, | ||
169 | &sensor_dev_attr_in2_max.dev_attr.attr, | ||
170 | &sensor_dev_attr_in2_label.dev_attr.attr, | ||
171 | |||
172 | &sensor_dev_attr_in3_input.dev_attr.attr, | ||
173 | &sensor_dev_attr_in3_min.dev_attr.attr, | ||
174 | &sensor_dev_attr_in3_max.dev_attr.attr, | ||
175 | &sensor_dev_attr_in3_label.dev_attr.attr, | ||
176 | |||
177 | &sensor_dev_attr_in4_input.dev_attr.attr, | ||
178 | &sensor_dev_attr_in4_min.dev_attr.attr, | ||
179 | &sensor_dev_attr_in4_max.dev_attr.attr, | ||
180 | &sensor_dev_attr_in4_label.dev_attr.attr, | ||
181 | NULL | ||
182 | }; | ||
183 | |||
184 | ATTRIBUTE_GROUPS(menf21bmc_hwmon); | ||
185 | |||
186 | static int menf21bmc_hwmon_probe(struct platform_device *pdev) | ||
187 | { | ||
188 | int ret; | ||
189 | struct menf21bmc_hwmon *drv_data; | ||
190 | struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent); | ||
191 | struct device *hwmon_dev; | ||
192 | |||
193 | drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon), | ||
194 | GFP_KERNEL); | ||
195 | if (!drv_data) | ||
196 | return -ENOMEM; | ||
197 | |||
198 | drv_data->i2c_client = i2c_client; | ||
199 | |||
200 | ret = menf21bmc_hwmon_get_volt_limits(drv_data); | ||
201 | if (ret) { | ||
202 | dev_err(&pdev->dev, "failed to read sensor limits"); | ||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, | ||
207 | "menf21bmc", drv_data, | ||
208 | menf21bmc_hwmon_groups); | ||
209 | if (IS_ERR(hwmon_dev)) | ||
210 | return PTR_ERR(hwmon_dev); | ||
211 | |||
212 | dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled"); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static struct platform_driver menf21bmc_hwmon = { | ||
218 | .probe = menf21bmc_hwmon_probe, | ||
219 | .driver = { | ||
220 | .name = DRV_NAME, | ||
221 | .owner = THIS_MODULE, | ||
222 | }, | ||
223 | }; | ||
224 | |||
225 | module_platform_driver(menf21bmc_hwmon); | ||
226 | |||
227 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | ||
228 | MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon"); | ||
229 | MODULE_LICENSE("GPL v2"); | ||
230 | MODULE_ALIAS("platform:menf21bmc_hwmon"); | ||
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index bd410722cd4b..4ff89b2482e4 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c | |||
@@ -38,6 +38,7 @@ | |||
38 | 38 | ||
39 | #include <linux/hwmon.h> | 39 | #include <linux/hwmon.h> |
40 | #include <linux/hwmon-sysfs.h> | 40 | #include <linux/hwmon-sysfs.h> |
41 | #include <linux/thermal.h> | ||
41 | 42 | ||
42 | struct ntc_compensation { | 43 | struct ntc_compensation { |
43 | int temp_c; | 44 | int temp_c; |
@@ -182,6 +183,7 @@ struct ntc_data { | |||
182 | struct device *dev; | 183 | struct device *dev; |
183 | int n_comp; | 184 | int n_comp; |
184 | char name[PLATFORM_NAME_SIZE]; | 185 | char name[PLATFORM_NAME_SIZE]; |
186 | struct thermal_zone_device *tz; | ||
185 | }; | 187 | }; |
186 | 188 | ||
187 | #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) | 189 | #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) |
@@ -428,6 +430,20 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data) | |||
428 | return -EINVAL; | 430 | return -EINVAL; |
429 | } | 431 | } |
430 | 432 | ||
433 | static int ntc_read_temp(void *dev, long *temp) | ||
434 | { | ||
435 | struct ntc_data *data = dev_get_drvdata(dev); | ||
436 | int ohm; | ||
437 | |||
438 | ohm = ntc_thermistor_get_ohm(data); | ||
439 | if (ohm < 0) | ||
440 | return ohm; | ||
441 | |||
442 | *temp = get_temp_mc(data, ohm); | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
431 | static ssize_t ntc_show_name(struct device *dev, | 447 | static ssize_t ntc_show_name(struct device *dev, |
432 | struct device_attribute *attr, char *buf) | 448 | struct device_attribute *attr, char *buf) |
433 | { | 449 | { |
@@ -562,6 +578,13 @@ static int ntc_thermistor_probe(struct platform_device *pdev) | |||
562 | dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", | 578 | dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", |
563 | pdev_id->name); | 579 | pdev_id->name); |
564 | 580 | ||
581 | data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev, | ||
582 | ntc_read_temp, NULL); | ||
583 | if (IS_ERR(data->tz)) { | ||
584 | dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); | ||
585 | data->tz = NULL; | ||
586 | } | ||
587 | |||
565 | return 0; | 588 | return 0; |
566 | err_after_sysfs: | 589 | err_after_sysfs: |
567 | sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); | 590 | sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); |
@@ -578,6 +601,8 @@ static int ntc_thermistor_remove(struct platform_device *pdev) | |||
578 | sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); | 601 | sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); |
579 | ntc_iio_channel_release(pdata); | 602 | ntc_iio_channel_release(pdata); |
580 | 603 | ||
604 | thermal_zone_of_sensor_unregister(data->dev, data->tz); | ||
605 | |||
581 | return 0; | 606 | return 0; |
582 | } | 607 | } |
583 | 608 | ||
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index bd89e87bd6ae..221f0931bf1c 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c | |||
@@ -100,8 +100,6 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; | |||
100 | 100 | ||
101 | struct smsc47b397_data { | 101 | struct smsc47b397_data { |
102 | unsigned short addr; | 102 | unsigned short addr; |
103 | const char *name; | ||
104 | struct device *hwmon_dev; | ||
105 | struct mutex lock; | 103 | struct mutex lock; |
106 | 104 | ||
107 | struct mutex update_lock; | 105 | struct mutex update_lock; |
@@ -202,15 +200,7 @@ static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); | |||
202 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); | 200 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); |
203 | static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); | 201 | static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); |
204 | 202 | ||
205 | static ssize_t show_name(struct device *dev, struct device_attribute | 203 | static struct attribute *smsc47b397_attrs[] = { |
206 | *devattr, char *buf) | ||
207 | { | ||
208 | struct smsc47b397_data *data = dev_get_drvdata(dev); | ||
209 | return sprintf(buf, "%s\n", data->name); | ||
210 | } | ||
211 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | ||
212 | |||
213 | static struct attribute *smsc47b397_attributes[] = { | ||
214 | &sensor_dev_attr_temp1_input.dev_attr.attr, | 204 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
215 | &sensor_dev_attr_temp2_input.dev_attr.attr, | 205 | &sensor_dev_attr_temp2_input.dev_attr.attr, |
216 | &sensor_dev_attr_temp3_input.dev_attr.attr, | 206 | &sensor_dev_attr_temp3_input.dev_attr.attr, |
@@ -220,23 +210,10 @@ static struct attribute *smsc47b397_attributes[] = { | |||
220 | &sensor_dev_attr_fan3_input.dev_attr.attr, | 210 | &sensor_dev_attr_fan3_input.dev_attr.attr, |
221 | &sensor_dev_attr_fan4_input.dev_attr.attr, | 211 | &sensor_dev_attr_fan4_input.dev_attr.attr, |
222 | 212 | ||
223 | &dev_attr_name.attr, | ||
224 | NULL | 213 | NULL |
225 | }; | 214 | }; |
226 | 215 | ||
227 | static const struct attribute_group smsc47b397_group = { | 216 | ATTRIBUTE_GROUPS(smsc47b397); |
228 | .attrs = smsc47b397_attributes, | ||
229 | }; | ||
230 | |||
231 | static int smsc47b397_remove(struct platform_device *pdev) | ||
232 | { | ||
233 | struct smsc47b397_data *data = platform_get_drvdata(pdev); | ||
234 | |||
235 | hwmon_device_unregister(data->hwmon_dev); | ||
236 | sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | 217 | ||
241 | static int smsc47b397_probe(struct platform_device *pdev); | 218 | static int smsc47b397_probe(struct platform_device *pdev); |
242 | 219 | ||
@@ -246,15 +223,14 @@ static struct platform_driver smsc47b397_driver = { | |||
246 | .name = DRVNAME, | 223 | .name = DRVNAME, |
247 | }, | 224 | }, |
248 | .probe = smsc47b397_probe, | 225 | .probe = smsc47b397_probe, |
249 | .remove = smsc47b397_remove, | ||
250 | }; | 226 | }; |
251 | 227 | ||
252 | static int smsc47b397_probe(struct platform_device *pdev) | 228 | static int smsc47b397_probe(struct platform_device *pdev) |
253 | { | 229 | { |
254 | struct device *dev = &pdev->dev; | 230 | struct device *dev = &pdev->dev; |
255 | struct smsc47b397_data *data; | 231 | struct smsc47b397_data *data; |
232 | struct device *hwmon_dev; | ||
256 | struct resource *res; | 233 | struct resource *res; |
257 | int err = 0; | ||
258 | 234 | ||
259 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 235 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
260 | if (!devm_request_region(dev, res->start, SMSC_EXTENT, | 236 | if (!devm_request_region(dev, res->start, SMSC_EXTENT, |
@@ -270,26 +246,13 @@ static int smsc47b397_probe(struct platform_device *pdev) | |||
270 | return -ENOMEM; | 246 | return -ENOMEM; |
271 | 247 | ||
272 | data->addr = res->start; | 248 | data->addr = res->start; |
273 | data->name = "smsc47b397"; | ||
274 | mutex_init(&data->lock); | 249 | mutex_init(&data->lock); |
275 | mutex_init(&data->update_lock); | 250 | mutex_init(&data->update_lock); |
276 | platform_set_drvdata(pdev, data); | ||
277 | |||
278 | err = sysfs_create_group(&dev->kobj, &smsc47b397_group); | ||
279 | if (err) | ||
280 | return err; | ||
281 | 251 | ||
282 | data->hwmon_dev = hwmon_device_register(dev); | 252 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, "smsc47b397", |
283 | if (IS_ERR(data->hwmon_dev)) { | 253 | data, |
284 | err = PTR_ERR(data->hwmon_dev); | 254 | smsc47b397_groups); |
285 | goto error_remove; | 255 | return PTR_ERR_OR_ZERO(hwmon_dev); |
286 | } | ||
287 | |||
288 | return 0; | ||
289 | |||
290 | error_remove: | ||
291 | sysfs_remove_group(&dev->kobj, &smsc47b397_group); | ||
292 | return err; | ||
293 | } | 256 | } |
294 | 257 | ||
295 | static int __init smsc47b397_device_add(unsigned short address) | 258 | static int __init smsc47b397_device_add(unsigned short address) |
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 90e108f9e22e..a210338cfeb1 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -468,6 +468,15 @@ config LEDS_OT200 | |||
468 | This option enables support for the LEDs on the Bachmann OT200. | 468 | This option enables support for the LEDs on the Bachmann OT200. |
469 | Say Y to enable LEDs on the Bachmann OT200. | 469 | Say Y to enable LEDs on the Bachmann OT200. |
470 | 470 | ||
471 | config LEDS_MENF21BMC | ||
472 | tristate "LED support for the MEN 14F021P00 BMC" | ||
473 | depends on LEDS_CLASS && MFD_MENF21BMC | ||
474 | help | ||
475 | Say Y here to include support for the MEN 14F021P00 BMC LEDs. | ||
476 | |||
477 | This driver can also be built as a module. If so the module | ||
478 | will be called leds-menf21bmc. | ||
479 | |||
471 | comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" | 480 | comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" |
472 | 481 | ||
473 | config LEDS_BLINKM | 482 | config LEDS_BLINKM |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 822dd83ef97a..a2b164741465 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -55,6 +55,7 @@ obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o | |||
55 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o | 55 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o |
56 | obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o | 56 | obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o |
57 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o | 57 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o |
58 | obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o | ||
58 | 59 | ||
59 | # LED SPI Drivers | 60 | # LED SPI Drivers |
60 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 61 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-menf21bmc.c b/drivers/leds/leds-menf21bmc.c new file mode 100644 index 000000000000..89dd57769e3b --- /dev/null +++ b/drivers/leds/leds-menf21bmc.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * MEN 14F021P00 Board Management Controller (BMC) LEDs Driver. | ||
3 | * | ||
4 | * This is the core LED driver of the MEN 14F021P00 BMC. | ||
5 | * There are four LEDs available which can be switched on and off. | ||
6 | * STATUS LED, HOT SWAP LED, USER LED 1, USER LED 2 | ||
7 | * | ||
8 | * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/leds.h> | ||
20 | #include <linux/i2c.h> | ||
21 | |||
22 | #define BMC_CMD_LED_GET_SET 0xA0 | ||
23 | #define BMC_BIT_LED_STATUS BIT(0) | ||
24 | #define BMC_BIT_LED_HOTSWAP BIT(1) | ||
25 | #define BMC_BIT_LED_USER1 BIT(2) | ||
26 | #define BMC_BIT_LED_USER2 BIT(3) | ||
27 | |||
28 | struct menf21bmc_led { | ||
29 | struct led_classdev cdev; | ||
30 | u8 led_bit; | ||
31 | const char *name; | ||
32 | struct i2c_client *i2c_client; | ||
33 | }; | ||
34 | |||
35 | static struct menf21bmc_led leds[] = { | ||
36 | { | ||
37 | .name = "menf21bmc:led_status", | ||
38 | .led_bit = BMC_BIT_LED_STATUS, | ||
39 | }, | ||
40 | { | ||
41 | .name = "menf21bmc:led_hotswap", | ||
42 | .led_bit = BMC_BIT_LED_HOTSWAP, | ||
43 | }, | ||
44 | { | ||
45 | .name = "menf21bmc:led_user1", | ||
46 | .led_bit = BMC_BIT_LED_USER1, | ||
47 | }, | ||
48 | { | ||
49 | .name = "menf21bmc:led_user2", | ||
50 | .led_bit = BMC_BIT_LED_USER2, | ||
51 | } | ||
52 | }; | ||
53 | |||
54 | static DEFINE_MUTEX(led_lock); | ||
55 | |||
56 | static void | ||
57 | menf21bmc_led_set(struct led_classdev *led_cdev, enum led_brightness value) | ||
58 | { | ||
59 | int led_val; | ||
60 | struct menf21bmc_led *led = container_of(led_cdev, | ||
61 | struct menf21bmc_led, cdev); | ||
62 | |||
63 | mutex_lock(&led_lock); | ||
64 | led_val = i2c_smbus_read_byte_data(led->i2c_client, | ||
65 | BMC_CMD_LED_GET_SET); | ||
66 | if (led_val < 0) | ||
67 | goto err_out; | ||
68 | |||
69 | if (value == LED_OFF) | ||
70 | led_val &= ~led->led_bit; | ||
71 | else | ||
72 | led_val |= led->led_bit; | ||
73 | |||
74 | i2c_smbus_write_byte_data(led->i2c_client, | ||
75 | BMC_CMD_LED_GET_SET, led_val); | ||
76 | err_out: | ||
77 | mutex_unlock(&led_lock); | ||
78 | } | ||
79 | |||
80 | static int menf21bmc_led_probe(struct platform_device *pdev) | ||
81 | { | ||
82 | int i; | ||
83 | int ret; | ||
84 | struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent); | ||
85 | |||
86 | for (i = 0; i < ARRAY_SIZE(leds); i++) { | ||
87 | leds[i].cdev.name = leds[i].name; | ||
88 | leds[i].cdev.brightness_set = menf21bmc_led_set; | ||
89 | leds[i].i2c_client = i2c_client; | ||
90 | ret = led_classdev_register(&pdev->dev, &leds[i].cdev); | ||
91 | if (ret < 0) | ||
92 | goto err_free_leds; | ||
93 | } | ||
94 | dev_info(&pdev->dev, "MEN 140F21P00 BMC LED device enabled\n"); | ||
95 | |||
96 | return 0; | ||
97 | |||
98 | err_free_leds: | ||
99 | dev_err(&pdev->dev, "failed to register LED device\n"); | ||
100 | |||
101 | for (i = i - 1; i >= 0; i--) | ||
102 | led_classdev_unregister(&leds[i].cdev); | ||
103 | |||
104 | return ret; | ||
105 | } | ||
106 | |||
107 | static int menf21bmc_led_remove(struct platform_device *pdev) | ||
108 | { | ||
109 | int i; | ||
110 | |||
111 | for (i = 0; i < ARRAY_SIZE(leds); i++) | ||
112 | led_classdev_unregister(&leds[i].cdev); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static struct platform_driver menf21bmc_led = { | ||
118 | .probe = menf21bmc_led_probe, | ||
119 | .remove = menf21bmc_led_remove, | ||
120 | .driver = { | ||
121 | .name = "menf21bmc_led", | ||
122 | .owner = THIS_MODULE, | ||
123 | }, | ||
124 | }; | ||
125 | |||
126 | module_platform_driver(menf21bmc_led); | ||
127 | |||
128 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | ||
129 | MODULE_DESCRIPTION("MEN 14F021P00 BMC led driver"); | ||
130 | MODULE_LICENSE("GPL v2"); | ||
131 | MODULE_ALIAS("platform:menf21bmc_led"); | ||
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index de5abf244746..cf66ef1ffaf3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -454,6 +454,21 @@ config MFD_MAX8998 | |||
454 | additional drivers must be enabled in order to use the functionality | 454 | additional drivers must be enabled in order to use the functionality |
455 | of the device. | 455 | of the device. |
456 | 456 | ||
457 | config MFD_MENF21BMC | ||
458 | tristate "MEN 14F021P00 Board Management Controller Support" | ||
459 | depends on I2C | ||
460 | select MFD_CORE | ||
461 | help | ||
462 | Say yes here to add support for the MEN 14F021P00 BMC | ||
463 | which is a Board Management Controller connected to the I2C bus. | ||
464 | The device supports multiple sub-devices like LED, HWMON and WDT. | ||
465 | This driver provides common support for accessing the devices; | ||
466 | additional drivers must be enabled in order to use the | ||
467 | functionality of the BMC device. | ||
468 | |||
469 | This driver can also be built as a module. If so the module | ||
470 | will be called menf21bmc. | ||
471 | |||
457 | config EZX_PCAP | 472 | config EZX_PCAP |
458 | bool "Motorola EZXPCAP Support" | 473 | bool "Motorola EZXPCAP Support" |
459 | depends on SPI_MASTER | 474 | depends on SPI_MASTER |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f00148782d9b..d58068aa8aa9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -169,6 +169,7 @@ obj-$(CONFIG_MFD_AS3711) += as3711.o | |||
169 | obj-$(CONFIG_MFD_AS3722) += as3722.o | 169 | obj-$(CONFIG_MFD_AS3722) += as3722.o |
170 | obj-$(CONFIG_MFD_STW481X) += stw481x.o | 170 | obj-$(CONFIG_MFD_STW481X) += stw481x.o |
171 | obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o | 171 | obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o |
172 | obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o | ||
172 | 173 | ||
173 | intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o | 174 | intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o |
174 | obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o | 175 | obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o |
diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c new file mode 100644 index 000000000000..1c274345820c --- /dev/null +++ b/drivers/mfd/menf21bmc.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver. | ||
3 | * | ||
4 | * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/i2c.h> | ||
16 | #include <linux/mfd/core.h> | ||
17 | |||
18 | #define BMC_CMD_WDT_EXIT_PROD 0x18 | ||
19 | #define BMC_CMD_WDT_PROD_STAT 0x19 | ||
20 | #define BMC_CMD_REV_MAJOR 0x80 | ||
21 | #define BMC_CMD_REV_MINOR 0x81 | ||
22 | #define BMC_CMD_REV_MAIN 0x82 | ||
23 | |||
24 | static struct mfd_cell menf21bmc_cell[] = { | ||
25 | { .name = "menf21bmc_wdt", }, | ||
26 | { .name = "menf21bmc_led", }, | ||
27 | { .name = "menf21bmc_hwmon", } | ||
28 | }; | ||
29 | |||
30 | static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client) | ||
31 | { | ||
32 | int val, ret; | ||
33 | |||
34 | val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT); | ||
35 | if (val < 0) | ||
36 | return val; | ||
37 | |||
38 | /* | ||
39 | * Production mode should be not active after delivery of the Board. | ||
40 | * To be sure we check it, inform the user and exit the mode | ||
41 | * if active. | ||
42 | */ | ||
43 | if (val == 0x00) { | ||
44 | dev_info(&client->dev, | ||
45 | "BMC in production mode. Exit production mode\n"); | ||
46 | |||
47 | ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD); | ||
48 | if (ret < 0) | ||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int | ||
56 | menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids) | ||
57 | { | ||
58 | int rev_major, rev_minor, rev_main; | ||
59 | int ret; | ||
60 | |||
61 | ret = i2c_check_functionality(client->adapter, | ||
62 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
63 | I2C_FUNC_SMBUS_WORD_DATA | | ||
64 | I2C_FUNC_SMBUS_BYTE); | ||
65 | if (!ret) | ||
66 | return -ENODEV; | ||
67 | |||
68 | rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR); | ||
69 | if (rev_major < 0) { | ||
70 | dev_err(&client->dev, "failed to get BMC major revision\n"); | ||
71 | return rev_major; | ||
72 | } | ||
73 | |||
74 | rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR); | ||
75 | if (rev_minor < 0) { | ||
76 | dev_err(&client->dev, "failed to get BMC minor revision\n"); | ||
77 | return rev_minor; | ||
78 | } | ||
79 | |||
80 | rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN); | ||
81 | if (rev_main < 0) { | ||
82 | dev_err(&client->dev, "failed to get BMC main revision\n"); | ||
83 | return rev_main; | ||
84 | } | ||
85 | |||
86 | dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n", | ||
87 | rev_major, rev_minor, rev_main); | ||
88 | |||
89 | /* | ||
90 | * We have to exit the Production Mode of the BMC to activate the | ||
91 | * Watchdog functionality and the BIOS life sign monitoring. | ||
92 | */ | ||
93 | ret = menf21bmc_wdt_exit_prod_mode(client); | ||
94 | if (ret < 0) { | ||
95 | dev_err(&client->dev, "failed to leave production mode\n"); | ||
96 | return ret; | ||
97 | } | ||
98 | |||
99 | ret = mfd_add_devices(&client->dev, 0, menf21bmc_cell, | ||
100 | ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL); | ||
101 | if (ret < 0) { | ||
102 | dev_err(&client->dev, "failed to add BMC sub-devices\n"); | ||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int menf21bmc_remove(struct i2c_client *client) | ||
110 | { | ||
111 | mfd_remove_devices(&client->dev); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static const struct i2c_device_id menf21bmc_id_table[] = { | ||
116 | { "menf21bmc" }, | ||
117 | { } | ||
118 | }; | ||
119 | MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table); | ||
120 | |||
121 | static struct i2c_driver menf21bmc_driver = { | ||
122 | .driver.name = "menf21bmc", | ||
123 | .id_table = menf21bmc_id_table, | ||
124 | .probe = menf21bmc_probe, | ||
125 | .remove = menf21bmc_remove, | ||
126 | }; | ||
127 | |||
128 | module_i2c_driver(menf21bmc_driver); | ||
129 | |||
130 | MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver"); | ||
131 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | ||
132 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1d1330a78af3..e3d5bf0a5021 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -95,6 +95,16 @@ config GPIO_WATCHDOG | |||
95 | If you say yes here you get support for watchdog device | 95 | If you say yes here you get support for watchdog device |
96 | controlled through GPIO-line. | 96 | controlled through GPIO-line. |
97 | 97 | ||
98 | config MENF21BMC_WATCHDOG | ||
99 | tristate "MEN 14F021P00 BMC Watchdog" | ||
100 | depends on MFD_MENF21BMC | ||
101 | select WATCHDOG_CORE | ||
102 | help | ||
103 | Say Y here to include support for the MEN 14F021P00 BMC Watchdog. | ||
104 | |||
105 | This driver can also be built as a module. If so the module | ||
106 | will be called menf21bmc_wdt. | ||
107 | |||
98 | config WM831X_WATCHDOG | 108 | config WM831X_WATCHDOG |
99 | tristate "WM831x watchdog" | 109 | tristate "WM831x watchdog" |
100 | depends on MFD_WM831X | 110 | depends on MFD_WM831X |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 468c3204c3b1..de1701470c14 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -178,3 +178,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | |||
178 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 178 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
179 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o | 179 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o |
180 | obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o | 180 | obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o |
181 | obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o | ||
diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c new file mode 100644 index 000000000000..2042874d5ce3 --- /dev/null +++ b/drivers/watchdog/menf21bmc_wdt.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver. | ||
3 | * | ||
4 | * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/watchdog.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/i2c.h> | ||
18 | |||
19 | #define DEVNAME "menf21bmc_wdt" | ||
20 | |||
21 | #define BMC_CMD_WD_ON 0x11 | ||
22 | #define BMC_CMD_WD_OFF 0x12 | ||
23 | #define BMC_CMD_WD_TRIG 0x13 | ||
24 | #define BMC_CMD_WD_TIME 0x14 | ||
25 | #define BMC_CMD_WD_STATE 0x17 | ||
26 | #define BMC_WD_OFF_VAL 0x69 | ||
27 | #define BMC_CMD_RST_RSN 0x92 | ||
28 | |||
29 | #define BMC_WD_TIMEOUT_MIN 1 /* in sec */ | ||
30 | #define BMC_WD_TIMEOUT_MAX 6553 /* in sec */ | ||
31 | |||
32 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
33 | module_param(nowayout, bool, 0); | ||
34 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
35 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
36 | |||
37 | struct menf21bmc_wdt { | ||
38 | struct watchdog_device wdt; | ||
39 | struct i2c_client *i2c_client; | ||
40 | }; | ||
41 | |||
42 | static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt *data) | ||
43 | { | ||
44 | int rst_rsn; | ||
45 | |||
46 | rst_rsn = i2c_smbus_read_byte_data(data->i2c_client, BMC_CMD_RST_RSN); | ||
47 | if (rst_rsn < 0) | ||
48 | return rst_rsn; | ||
49 | |||
50 | if (rst_rsn == 0x02) | ||
51 | data->wdt.bootstatus |= WDIOF_CARDRESET; | ||
52 | else if (rst_rsn == 0x05) | ||
53 | data->wdt.bootstatus |= WDIOF_EXTERN1; | ||
54 | else if (rst_rsn == 0x06) | ||
55 | data->wdt.bootstatus |= WDIOF_EXTERN2; | ||
56 | else if (rst_rsn == 0x0A) | ||
57 | data->wdt.bootstatus |= WDIOF_POWERUNDER; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int menf21bmc_wdt_start(struct watchdog_device *wdt) | ||
63 | { | ||
64 | struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt); | ||
65 | |||
66 | return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_ON); | ||
67 | } | ||
68 | |||
69 | static int menf21bmc_wdt_stop(struct watchdog_device *wdt) | ||
70 | { | ||
71 | struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt); | ||
72 | |||
73 | return i2c_smbus_write_byte_data(drv_data->i2c_client, | ||
74 | BMC_CMD_WD_OFF, BMC_WD_OFF_VAL); | ||
75 | } | ||
76 | |||
77 | static int | ||
78 | menf21bmc_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout) | ||
79 | { | ||
80 | int ret; | ||
81 | struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt); | ||
82 | |||
83 | /* | ||
84 | * BMC Watchdog does have a resolution of 100ms. | ||
85 | * Watchdog API defines the timeout in seconds, so we have to | ||
86 | * multiply the value. | ||
87 | */ | ||
88 | ret = i2c_smbus_write_word_data(drv_data->i2c_client, | ||
89 | BMC_CMD_WD_TIME, timeout * 10); | ||
90 | if (ret < 0) | ||
91 | return ret; | ||
92 | |||
93 | wdt->timeout = timeout; | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int menf21bmc_wdt_ping(struct watchdog_device *wdt) | ||
99 | { | ||
100 | struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt); | ||
101 | |||
102 | return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_TRIG); | ||
103 | } | ||
104 | |||
105 | static const struct watchdog_info menf21bmc_wdt_info = { | ||
106 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
107 | .identity = DEVNAME, | ||
108 | }; | ||
109 | |||
110 | static const struct watchdog_ops menf21bmc_wdt_ops = { | ||
111 | .owner = THIS_MODULE, | ||
112 | .start = menf21bmc_wdt_start, | ||
113 | .stop = menf21bmc_wdt_stop, | ||
114 | .ping = menf21bmc_wdt_ping, | ||
115 | .set_timeout = menf21bmc_wdt_settimeout, | ||
116 | }; | ||
117 | |||
118 | static int menf21bmc_wdt_probe(struct platform_device *pdev) | ||
119 | { | ||
120 | int ret, bmc_timeout; | ||
121 | struct menf21bmc_wdt *drv_data; | ||
122 | struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent); | ||
123 | |||
124 | drv_data = devm_kzalloc(&pdev->dev, | ||
125 | sizeof(struct menf21bmc_wdt), GFP_KERNEL); | ||
126 | if (!drv_data) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | drv_data->wdt.ops = &menf21bmc_wdt_ops; | ||
130 | drv_data->wdt.info = &menf21bmc_wdt_info; | ||
131 | drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN; | ||
132 | drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX; | ||
133 | drv_data->i2c_client = i2c_client; | ||
134 | |||
135 | /* | ||
136 | * Get the current wdt timeout value from the BMC because | ||
137 | * the BMC will save the value set before if the system restarts. | ||
138 | */ | ||
139 | bmc_timeout = i2c_smbus_read_word_data(drv_data->i2c_client, | ||
140 | BMC_CMD_WD_TIME); | ||
141 | if (bmc_timeout < 0) { | ||
142 | dev_err(&pdev->dev, "failed to get current WDT timeout\n"); | ||
143 | return bmc_timeout; | ||
144 | } | ||
145 | |||
146 | watchdog_init_timeout(&drv_data->wdt, bmc_timeout / 10, &pdev->dev); | ||
147 | watchdog_set_nowayout(&drv_data->wdt, nowayout); | ||
148 | watchdog_set_drvdata(&drv_data->wdt, drv_data); | ||
149 | platform_set_drvdata(pdev, drv_data); | ||
150 | |||
151 | ret = menf21bmc_wdt_set_bootstatus(drv_data); | ||
152 | if (ret < 0) { | ||
153 | dev_err(&pdev->dev, "failed to set Watchdog bootstatus\n"); | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | ret = watchdog_register_device(&drv_data->wdt); | ||
158 | if (ret) { | ||
159 | dev_err(&pdev->dev, "failed to register Watchdog device\n"); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | dev_info(&pdev->dev, "MEN 14F021P00 BMC Watchdog device enabled\n"); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int menf21bmc_wdt_remove(struct platform_device *pdev) | ||
169 | { | ||
170 | struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev); | ||
171 | |||
172 | dev_warn(&pdev->dev, | ||
173 | "Unregister MEN 14F021P00 BMC Watchdog device, board may reset\n"); | ||
174 | |||
175 | watchdog_unregister_device(&drv_data->wdt); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static void menf21bmc_wdt_shutdown(struct platform_device *pdev) | ||
181 | { | ||
182 | struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev); | ||
183 | |||
184 | i2c_smbus_write_word_data(drv_data->i2c_client, | ||
185 | BMC_CMD_WD_OFF, BMC_WD_OFF_VAL); | ||
186 | } | ||
187 | |||
188 | static struct platform_driver menf21bmc_wdt = { | ||
189 | .driver = { | ||
190 | .owner = THIS_MODULE, | ||
191 | .name = DEVNAME, | ||
192 | }, | ||
193 | .probe = menf21bmc_wdt_probe, | ||
194 | .remove = menf21bmc_wdt_remove, | ||
195 | .shutdown = menf21bmc_wdt_shutdown, | ||
196 | }; | ||
197 | |||
198 | module_platform_driver(menf21bmc_wdt); | ||
199 | |||
200 | MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver"); | ||
201 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | ||
202 | MODULE_LICENSE("GPL v2"); | ||
203 | MODULE_ALIAS("platform:menf21bmc_wdt"); | ||