diff options
-rw-r--r-- | Documentation/hwmon/menf21bmc | 50 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/menf21bmc_hwmon.c | 230 | ||||
-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 |
13 files changed, 794 insertions, 0 deletions
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/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f790b41283d0..5286d7ce1f9e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -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 |
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/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/leds/Kconfig b/drivers/leds/Kconfig index 8c96e2ddf43b..eadd56c91551 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 d8cc5f2777de..9a72fddc1d78 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o | |||
54 | obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o | 54 | 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_VERSATILE) += leds-versatile.o | 56 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o |
57 | obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o | ||
57 | 58 | ||
58 | # LED SPI Drivers | 59 | # LED SPI Drivers |
59 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 60 | 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 f57312fced80..a50828e34e3b 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"); | ||