aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/hwmon/menf21bmc50
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/menf21bmc_hwmon.c230
-rw-r--r--drivers/leds/Kconfig9
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-menf21bmc.c131
-rw-r--r--drivers/mfd/Kconfig15
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/menf21bmc.c132
-rw-r--r--drivers/watchdog/Kconfig10
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/menf21bmc_wdt.c203
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 @@
1Kernel driver menf21bmc_hwmon
2=============================
3
4Supported chips:
5 * MEN 14F021P00
6 Prefix: 'menf21bmc_hwmon'
7 Adresses scanned: -
8
9Author: Andreas Werner <andreas.werner@men.de>
10
11Description
12-----------
13
14The menf21bmc is a Board Management Controller (BMC) which provides an I2C
15interface to the host to access the features implemented in the BMC.
16
17This driver gives access to the voltage monitoring feature of the main
18voltages of the board.
19The voltage sensors are connected to the ADC inputs of the BMC which is
20a PIC16F917 Mikrocontroller.
21
22Usage Notes
23-----------
24
25This driver is part of the MFD driver named "menf21bmc" and does
26not auto-detect devices.
27You will have to instantiate the MFD driver explicitly.
28Please see Documentation/i2c/instantiating-devices for
29details.
30
31Sysfs entries
32-------------
33
34The following attributes are supported. All attributes are read only
35The Limits are read once by the driver.
36
37in0_input +3.3V input voltage
38in1_input +5.0V input voltage
39in2_input +12.0V input voltage
40in3_input +5V Standby input voltage
41in4_input VBAT (on board battery)
42
43in[0-4]_min Minimum voltage limit
44in[0-4]_max Maximum voltage limit
45
46in0_label "MON_3_3V"
47in1_label "MON_5V"
48in2_label "MON_12V"
49in3_label "5V_STANDBY"
50in4_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
842config 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
842config SENSORS_ADCXX 852config 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
115obj-$(CONFIG_SENSORS_MAX6697) += max6697.o 115obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
116obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 116obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
117obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o 117obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
118obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
118obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o 119obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
119obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o 120obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
120obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o 121obj-$(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
38struct 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
47static 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
55static 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 }
76abort:
77 return data_ret;
78}
79
80static 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
102static ssize_t
103show_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
110static ssize_t
111show_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
122static ssize_t
123show_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
131static ssize_t
132show_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) \
141static SENSOR_DEVICE_ATTR(in##idx##_input, S_IRUGO, \
142 show_in, NULL, idx); \
143static SENSOR_DEVICE_ATTR(in##idx##_min, S_IRUGO, \
144 show_min, NULL, idx); \
145static SENSOR_DEVICE_ATTR(in##idx##_max, S_IRUGO, \
146 show_max, NULL, idx); \
147static SENSOR_DEVICE_ATTR(in##idx##_label, S_IRUGO, \
148 show_label, NULL, idx);
149
150create_voltage_sysfs(0);
151create_voltage_sysfs(1);
152create_voltage_sysfs(2);
153create_voltage_sysfs(3);
154create_voltage_sysfs(4);
155
156static 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
184ATTRIBUTE_GROUPS(menf21bmc_hwmon);
185
186static 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
217static struct platform_driver menf21bmc_hwmon = {
218 .probe = menf21bmc_hwmon_probe,
219 .driver = {
220 .name = DRV_NAME,
221 .owner = THIS_MODULE,
222 },
223};
224
225module_platform_driver(menf21bmc_hwmon);
226
227MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
228MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon");
229MODULE_LICENSE("GPL v2");
230MODULE_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
471config 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
471comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" 480comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
472 481
473config LEDS_BLINKM 482config 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
54obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o 54obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
55obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o 55obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
56obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o 56obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
57obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
57 58
58# LED SPI Drivers 59# LED SPI Drivers
59obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o 60obj-$(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
28struct menf21bmc_led {
29 struct led_classdev cdev;
30 u8 led_bit;
31 const char *name;
32 struct i2c_client *i2c_client;
33};
34
35static 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
54static DEFINE_MUTEX(led_lock);
55
56static void
57menf21bmc_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);
76err_out:
77 mutex_unlock(&led_lock);
78}
79
80static 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
98err_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
107static 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
117static 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
126module_platform_driver(menf21bmc_led);
127
128MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
129MODULE_DESCRIPTION("MEN 14F021P00 BMC led driver");
130MODULE_LICENSE("GPL v2");
131MODULE_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
457config 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
457config EZX_PCAP 472config 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
169obj-$(CONFIG_MFD_AS3722) += as3722.o 169obj-$(CONFIG_MFD_AS3722) += as3722.o
170obj-$(CONFIG_MFD_STW481X) += stw481x.o 170obj-$(CONFIG_MFD_STW481X) += stw481x.o
171obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o 171obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
172obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o
172 173
173intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o 174intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
174obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o 175obj-$(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
24static struct mfd_cell menf21bmc_cell[] = {
25 { .name = "menf21bmc_wdt", },
26 { .name = "menf21bmc_led", },
27 { .name = "menf21bmc_hwmon", }
28};
29
30static 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
55static int
56menf21bmc_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
109static int menf21bmc_remove(struct i2c_client *client)
110{
111 mfd_remove_devices(&client->dev);
112 return 0;
113}
114
115static const struct i2c_device_id menf21bmc_id_table[] = {
116 { "menf21bmc" },
117 { }
118};
119MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table);
120
121static 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
128module_i2c_driver(menf21bmc_driver);
129
130MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver");
131MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
132MODULE_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
98config 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
98config WM831X_WATCHDOG 108config 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
178obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 178obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
179obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o 179obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
180obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 180obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
181obj-$(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
32static bool nowayout = WATCHDOG_NOWAYOUT;
33module_param(nowayout, bool, 0);
34MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
35 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
36
37struct menf21bmc_wdt {
38 struct watchdog_device wdt;
39 struct i2c_client *i2c_client;
40};
41
42static 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
62static 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
69static 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
77static int
78menf21bmc_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
98static 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
105static const struct watchdog_info menf21bmc_wdt_info = {
106 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
107 .identity = DEVNAME,
108};
109
110static 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
118static 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
168static 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
180static 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
188static 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
198module_platform_driver(menf21bmc_wdt);
199
200MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
201MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
202MODULE_LICENSE("GPL v2");
203MODULE_ALIAS("platform:menf21bmc_wdt");