Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'mfd-hwmon-leds-watchdog-v3.18' into hwmon-next

Immutable branch between MFD, HWMON, LEDs and Watchdog for v3.18

+794
+50
Documentation/hwmon/menf21bmc
··· 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"
+10
drivers/hwmon/Kconfig
··· 839 839 This driver can also be built as a module. If so, the module 840 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 852 config SENSORS_ADCXX 843 853 tristate "National Semiconductor ADCxxxSxxx" 844 854 depends on SPI_MASTER
+1
drivers/hwmon/Makefile
··· 115 115 obj-$(CONFIG_SENSORS_MAX6697) += max6697.o 116 116 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 117 117 obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o 118 + obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o 118 119 obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o 119 120 obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o 120 121 obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
+230
drivers/hwmon/menf21bmc_hwmon.c
··· 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");
+9
drivers/leds/Kconfig
··· 468 468 This option enables support for the LEDs on the Bachmann OT200. 469 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 480 comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" 472 481 473 482 config LEDS_BLINKM
+1
drivers/leds/Makefile
··· 54 54 obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o 55 55 obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o 56 56 obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o 57 + obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o 57 58 58 59 # LED SPI Drivers 59 60 obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
+131
drivers/leds/leds-menf21bmc.c
··· 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");
+15
drivers/mfd/Kconfig
··· 454 454 additional drivers must be enabled in order to use the functionality 455 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 472 config EZX_PCAP 458 473 bool "Motorola EZXPCAP Support" 459 474 depends on SPI_MASTER
+1
drivers/mfd/Makefile
··· 169 169 obj-$(CONFIG_MFD_AS3722) += as3722.o 170 170 obj-$(CONFIG_MFD_STW481X) += stw481x.o 171 171 obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o 172 + obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o 172 173 173 174 intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o 174 175 obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+132
drivers/mfd/menf21bmc.c
··· 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");
+10
drivers/watchdog/Kconfig
··· 95 95 If you say yes here you get support for watchdog device 96 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 108 config WM831X_WATCHDOG 99 109 tristate "WM831x watchdog" 100 110 depends on MFD_WM831X
+1
drivers/watchdog/Makefile
··· 178 178 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 179 179 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o 180 180 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 181 + obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
+203
drivers/watchdog/menf21bmc_wdt.c
··· 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");