diff options
| author | Guenter Roeck <linux@roeck-us.net> | 2014-09-24 12:25:06 -0400 |
|---|---|---|
| committer | Guenter Roeck <linux@roeck-us.net> | 2014-09-24 12:25:06 -0400 |
| commit | 7ad8966f4f0a4606dfaba119ea4e2b3ac4c548ee (patch) | |
| tree | 38a7548d5a36cc20145eb916319fe673b84341b1 | |
| parent | c08860ffe5c0e986e208e8217dae8191c0b40b24 (diff) | |
| parent | 964356938fcd3c0001a786f55b9f0a0fbe47656a (diff) | |
Merge tag 'mfd-hwmon-leds-watchdog-v3.18' into hwmon-next
Immutable branch between MFD, HWMON, LEDs and Watchdog for v3.18
| -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"); | ||
