Merge branches 'ib-mfd-arm-3.18', 'ib-mfd-hwmon-leds-watchdog-3.18' and 'ib-mfd-power...
authorLee Jones <lee.jones@linaro.org>
Fri, 26 Sep 2014 07:14:30 +0000 (08:14 +0100)
committerLee Jones <lee.jones@linaro.org>
Fri, 26 Sep 2014 07:14:30 +0000 (08:14 +0100)
22 files changed:
Documentation/ABI/testing/sysfs-class-power
Documentation/devicetree/bindings/mfd/max14577.txt [new file with mode: 0644]
Documentation/hwmon/menf21bmc [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/menf21bmc_hwmon.c [new file with mode: 0644]
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-menf21bmc.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/max14577.c
drivers/mfd/menf21bmc.c [new file with mode: 0644]
drivers/power/Kconfig
drivers/power/max14577_charger.c
drivers/power/max17040_battery.c
drivers/regulator/max14577.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/menf21bmc_wdt.c [new file with mode: 0644]
include/linux/mfd/max14577-private.h
include/linux/mfd/max14577.h

index 78c7baca35879103d6de56d08e1c7bd4724bc9aa..ad4fb90ab23df9b6c4ba6ed48c43b58bed27610e 100644 (file)
@@ -18,3 +18,17 @@ Description:
                This file is writeable and can be used to set the assumed
                battery 'full level'. As batteries age, this value has to be
                amended over time.
+
+What:          /sys/class/power_supply/max14577-charger/device/fast_charge_timer
+Date:          July 2014
+KernelVersion: 3.18.0
+Contact:       Krzysztof Kozlowski <k.kozlowski@samsung.com>
+Description:
+               This entry shows and sets the maximum time the max14577
+               charger operates in fast-charge mode. When the timer expires
+               the device will terminate fast-charge mode (charging current
+               will drop to 0 A) and will trigger interrupt.
+
+               Valid values:
+               - 5, 6 or 7 (hours),
+               - 0: disabled.
diff --git a/Documentation/devicetree/bindings/mfd/max14577.txt b/Documentation/devicetree/bindings/mfd/max14577.txt
new file mode 100644 (file)
index 0000000..236264c
--- /dev/null
@@ -0,0 +1,146 @@
+Maxim MAX14577/77836 Multi-Function Device
+
+MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+
+Battery Charger and SFOUT LDO output for powering USB devices. It is
+interfaced to host controller using I2C.
+
+MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge.
+
+
+Required properties:
+- compatible : Must be "maxim,max14577" or "maxim,max77836".
+- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836)
+- interrupts : IRQ line for the chip.
+- interrupt-parent :  The parent interrupt controller.
+
+
+Required nodes:
+ - charger :
+       Node for configuring the charger driver.
+       Required properties:
+               - compatible : "maxim,max14577-charger"
+                       or "maxim,max77836-charger"
+               - maxim,fast-charge-uamp : Current in uA for Fast Charge;
+                       Valid values:
+                       - for max14577: 90000 - 950000;
+                       - for max77836: 45000 - 475000;
+               - maxim,eoc-uamp : Current in uA for End-Of-Charge mode;
+                       Valid values:
+                       - for max14577: 50000 - 200000;
+                       - for max77836: 5000 - 100000;
+               - maxim,ovp-uvolt : OverVoltage Protection Threshold in uV;
+                       In an overvoltage condition, INT asserts and charging
+                       stops. Valid values:
+                       - 6000000, 6500000, 7000000, 7500000;
+               - maxim,constant-uvolt : Battery Constant Voltage in uV;
+                       Valid values:
+                       - 4000000 - 4280000 (step by 20000);
+                       - 4350000;
+
+
+Optional nodes:
+- max14577-muic/max77836-muic :
+       Node used only by extcon consumers.
+       Required properties:
+               - compatible : "maxim,max14577-muic" or "maxim,max77836-muic"
+
+- regulators :
+       Required properties:
+               - compatible : "maxim,max14577-regulator"
+                       or "maxim,max77836-regulator"
+
+       May contain a sub-node per regulator from the list below. Each
+       sub-node should contain the constraints and initialization information
+       for that regulator. See regulator.txt for a description of standard
+       properties for these sub-nodes.
+
+       List of valid regulator names:
+       - for max14577: CHARGER, SAFEOUT.
+       - for max77836: CHARGER, SAFEOUT, LDO1, LDO2.
+
+       The SAFEOUT is a fixed voltage regulator so there is no need to specify
+       voltages for it.
+
+
+Example:
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+max14577@25 {
+       compatible = "maxim,max14577";
+       reg = <0x25>;
+       interrupt-parent = <&gpx1>;
+       interrupts = <5 IRQ_TYPE_NONE>;
+
+       muic: max14577-muic {
+               compatible = "maxim,max14577-muic";
+       };
+
+       regulators {
+               compatible = "maxim,max14577-regulator";
+
+               SAFEOUT {
+                       regulator-name = "SAFEOUT";
+               };
+               CHARGER {
+                       regulator-name = "CHARGER";
+                       regulator-min-microamp = <90000>;
+                       regulator-max-microamp = <950000>;
+                       regulator-boot-on;
+               };
+       };
+
+       charger {
+               compatible = "maxim,max14577-charger";
+
+               maxim,constant-uvolt = <4350000>;
+               maxim,fast-charge-uamp = <450000>;
+               maxim,eoc-uamp = <50000>;
+               maxim,ovp-uvolt = <6500000>;
+       };
+};
+
+
+max77836@25 {
+       compatible = "maxim,max77836";
+       reg = <0x25>;
+       interrupt-parent = <&gpx1>;
+       interrupts = <5 IRQ_TYPE_NONE>;
+
+       muic: max77836-muic {
+               compatible = "maxim,max77836-muic";
+       };
+
+       regulators {
+               compatible = "maxim,max77836-regulator";
+
+               SAFEOUT {
+                       regulator-name = "SAFEOUT";
+               };
+               CHARGER {
+                       regulator-name = "CHARGER";
+                       regulator-min-microamp = <90000>;
+                       regulator-max-microamp = <950000>;
+                       regulator-boot-on;
+               };
+               LDO1 {
+                       regulator-name = "LDO1";
+                       regulator-min-microvolt = <2700000>;
+                       regulator-max-microvolt = <2700000>;
+               };
+               LDO2 {
+                       regulator-name = "LDO2";
+                       regulator-min-microvolt = <800000>;
+                       regulator-max-microvolt = <3950000>;
+               };
+       };
+
+       charger {
+               compatible = "maxim,max77836-charger";
+
+               maxim,constant-uvolt = <4350000>;
+               maxim,fast-charge-uamp = <225000>;
+               maxim,eoc-uamp = <7500>;
+               maxim,ovp-uvolt = <6500000>;
+       };
+};
diff --git a/Documentation/hwmon/menf21bmc b/Documentation/hwmon/menf21bmc
new file mode 100644 (file)
index 0000000..2a273a0
--- /dev/null
@@ -0,0 +1,50 @@
+Kernel driver menf21bmc_hwmon
+=============================
+
+Supported chips:
+       * MEN 14F021P00
+         Prefix: 'menf21bmc_hwmon'
+         Adresses scanned: -
+
+Author: Andreas Werner <andreas.werner@men.de>
+
+Description
+-----------
+
+The menf21bmc is a Board Management Controller (BMC) which provides an I2C
+interface to the host to access the features implemented in the BMC.
+
+This driver gives access to the voltage monitoring feature of the main
+voltages of the board.
+The voltage sensors are connected to the ADC inputs of the BMC which is
+a PIC16F917 Mikrocontroller.
+
+Usage Notes
+-----------
+
+This driver is part of the MFD driver named "menf21bmc" and does
+not auto-detect devices.
+You will have to instantiate the MFD driver explicitly.
+Please see Documentation/i2c/instantiating-devices for
+details.
+
+Sysfs entries
+-------------
+
+The following attributes are supported. All attributes are read only
+The Limits are read once by the driver.
+
+in0_input      +3.3V input voltage
+in1_input      +5.0V input voltage
+in2_input      +12.0V input voltage
+in3_input      +5V Standby input voltage
+in4_input      VBAT (on board battery)
+
+in[0-4]_min    Minimum voltage limit
+in[0-4]_max    Maximum voltage limit
+
+in0_label      "MON_3_3V"
+in1_label      "MON_5V"
+in2_label      "MON_12V"
+in3_label      "5V_STANDBY"
+in4_label      "VBAT"
index f00d048aa583591c45aca5a7e683002c5bbc4721..c08d172bbab44df7052ca985161aab0d0b8dba41 100644 (file)
@@ -839,6 +839,16 @@ config SENSORS_MCP3021
          This driver can also be built as a module.  If so, the module
          will be called mcp3021.
 
+config SENSORS_MENF21BMC_HWMON
+       tristate "MEN 14F021P00 BMC Hardware Monitoring"
+       depends on MFD_MENF21BMC
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC
+         hardware monitoring.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc_hwmon.
+
 config SENSORS_ADCXX
        tristate "National Semiconductor ADCxxxSxxx"
        depends on SPI_MASTER
index be28152c9848806391f58abecce93914f7edf384..c90a7611efaabc26c35c8b66cc90fbe50b4e9c96 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6650)       += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
+obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
 obj-$(CONFIG_SENSORS_NCT6683)  += nct6683.o
 obj-$(CONFIG_SENSORS_NCT6775)  += nct6775.o
 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 (file)
index 0000000..c92229d
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) hwmon driver.
+ *
+ *  This is the core hwmon driver of the MEN 14F021P00 BMC.
+ *  The BMC monitors the board voltages which can be access with this
+ *  driver through sysfs.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#define DRV_NAME  "menf21bmc_hwmon"
+
+#define BMC_VOLT_COUNT 5
+#define MENF21BMC_V33  0
+#define MENF21BMC_V5   1
+#define MENF21BMC_V12  2
+#define MENF21BMC_V5_SB        3
+#define MENF21BMC_VBAT 4
+
+#define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx)
+#define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx)
+#define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx)
+
+struct menf21bmc_hwmon {
+       bool valid;
+       struct i2c_client *i2c_client;
+       unsigned long last_update;
+       int in_val[BMC_VOLT_COUNT];
+       int in_min[BMC_VOLT_COUNT];
+       int in_max[BMC_VOLT_COUNT];
+};
+
+static const char *const input_names[] = {
+       [MENF21BMC_V33]         = "MON_3_3V",
+       [MENF21BMC_V5]          = "MON_5V",
+       [MENF21BMC_V12]         = "MON_12V",
+       [MENF21BMC_V5_SB]       = "5V_STANDBY",
+       [MENF21BMC_VBAT]        = "VBAT"
+};
+
+static struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev)
+{
+       int i;
+       int val;
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+       struct menf21bmc_hwmon *data_ret = drv_data;
+
+       if (time_after(jiffies, drv_data->last_update + HZ)
+           || !drv_data->valid) {
+               for (i = 0; i < BMC_VOLT_COUNT; i++) {
+                       val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                                      IDX_TO_VOLT_INP_CMD(i));
+                       if (val < 0) {
+                               data_ret = ERR_PTR(val);
+                               goto abort;
+                       }
+                       drv_data->in_val[i] = val;
+               }
+               drv_data->last_update = jiffies;
+               drv_data->valid = true;
+       }
+abort:
+       return data_ret;
+}
+
+static int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data)
+{
+       int i, val;
+
+       for (i = 0; i < BMC_VOLT_COUNT; i++) {
+               val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              IDX_TO_VOLT_MIN_CMD(i));
+               if (val < 0)
+                       return val;
+
+               drv_data->in_min[i] = val;
+
+               val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              IDX_TO_VOLT_MAX_CMD(i));
+               if (val < 0)
+                       return val;
+
+               drv_data->in_max[i] = val;
+       }
+       return 0;
+}
+
+static ssize_t
+show_label(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%s\n", input_names[attr->index]);
+}
+
+static ssize_t
+show_in(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev);
+
+       if (IS_ERR(drv_data))
+               return PTR_ERR(drv_data);
+
+       return sprintf(buf, "%d\n", drv_data->in_val[attr->index]);
+}
+
+static ssize_t
+show_min(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", drv_data->in_min[attr->index]);
+}
+
+static ssize_t
+show_max(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", drv_data->in_max[attr->index]);
+}
+
+#define create_voltage_sysfs(idx)                      \
+static SENSOR_DEVICE_ATTR(in##idx##_input, S_IRUGO,    \
+                       show_in, NULL, idx);            \
+static SENSOR_DEVICE_ATTR(in##idx##_min, S_IRUGO,      \
+                       show_min, NULL, idx);           \
+static SENSOR_DEVICE_ATTR(in##idx##_max, S_IRUGO,      \
+                       show_max, NULL, idx);           \
+static SENSOR_DEVICE_ATTR(in##idx##_label, S_IRUGO,    \
+                       show_label, NULL, idx);
+
+create_voltage_sysfs(0);
+create_voltage_sysfs(1);
+create_voltage_sysfs(2);
+create_voltage_sysfs(3);
+create_voltage_sysfs(4);
+
+static struct attribute *menf21bmc_hwmon_attrs[] = {
+       &sensor_dev_attr_in0_input.dev_attr.attr,
+       &sensor_dev_attr_in0_min.dev_attr.attr,
+       &sensor_dev_attr_in0_max.dev_attr.attr,
+       &sensor_dev_attr_in0_label.dev_attr.attr,
+
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_min.dev_attr.attr,
+       &sensor_dev_attr_in1_max.dev_attr.attr,
+       &sensor_dev_attr_in1_label.dev_attr.attr,
+
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in2_min.dev_attr.attr,
+       &sensor_dev_attr_in2_max.dev_attr.attr,
+       &sensor_dev_attr_in2_label.dev_attr.attr,
+
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in3_min.dev_attr.attr,
+       &sensor_dev_attr_in3_max.dev_attr.attr,
+       &sensor_dev_attr_in3_label.dev_attr.attr,
+
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       &sensor_dev_attr_in4_min.dev_attr.attr,
+       &sensor_dev_attr_in4_max.dev_attr.attr,
+       &sensor_dev_attr_in4_label.dev_attr.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(menf21bmc_hwmon);
+
+static int menf21bmc_hwmon_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct menf21bmc_hwmon *drv_data;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+       struct device *hwmon_dev;
+
+       drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon),
+                               GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->i2c_client = i2c_client;
+
+       ret = menf21bmc_hwmon_get_volt_limits(drv_data);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to read sensor limits");
+               return ret;
+       }
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+                                                  "menf21bmc", drv_data,
+                                                  menf21bmc_hwmon_groups);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+
+       dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled");
+
+       return 0;
+}
+
+static struct platform_driver menf21bmc_hwmon = {
+       .probe          = menf21bmc_hwmon_probe,
+       .driver         = {
+               .name           = DRV_NAME,
+               .owner          = THIS_MODULE,
+       },
+};
+
+module_platform_driver(menf21bmc_hwmon);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_hwmon");
index 8c96e2ddf43bf035ce59d4453b2aac38e151cb7f..eadd56c91551aed0e86c1e660a6227dbf786757c 100644 (file)
@@ -468,6 +468,15 @@ config LEDS_OT200
          This option enables support for the LEDs on the Bachmann OT200.
          Say Y to enable LEDs on the Bachmann OT200.
 
+config LEDS_MENF21BMC
+       tristate "LED support for the MEN 14F021P00 BMC"
+       depends on LEDS_CLASS && MFD_MENF21BMC
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC LEDs.
+
+         This driver can also be built as a module. If so the module
+         will be called leds-menf21bmc.
+
 comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
 
 config LEDS_BLINKM
index d8cc5f2777de94e880da92d6fdde7894cf8877c8..9a72fddc1d78ff1cfc54e0cdcba226f18a862b24 100644 (file)
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_MAX8997)            += leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)              += leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)              += leds-blinkm.o
 obj-$(CONFIG_LEDS_VERSATILE)           += leds-versatile.o
+obj-$(CONFIG_LEDS_MENF21BMC)           += leds-menf21bmc.o
 
 # LED SPI Drivers
 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 (file)
index 0000000..89dd577
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) LEDs Driver.
+ *
+ *  This is the core LED driver of the MEN 14F021P00 BMC.
+ *  There are four LEDs available which can be switched on and off.
+ *  STATUS LED, HOT SWAP LED, USER LED 1, USER LED 2
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/i2c.h>
+
+#define BMC_CMD_LED_GET_SET    0xA0
+#define BMC_BIT_LED_STATUS     BIT(0)
+#define BMC_BIT_LED_HOTSWAP    BIT(1)
+#define BMC_BIT_LED_USER1      BIT(2)
+#define BMC_BIT_LED_USER2      BIT(3)
+
+struct menf21bmc_led {
+       struct led_classdev cdev;
+       u8 led_bit;
+       const char *name;
+       struct i2c_client *i2c_client;
+};
+
+static struct menf21bmc_led leds[] = {
+       {
+               .name = "menf21bmc:led_status",
+               .led_bit = BMC_BIT_LED_STATUS,
+       },
+       {
+               .name = "menf21bmc:led_hotswap",
+               .led_bit = BMC_BIT_LED_HOTSWAP,
+       },
+       {
+               .name = "menf21bmc:led_user1",
+               .led_bit = BMC_BIT_LED_USER1,
+       },
+       {
+               .name = "menf21bmc:led_user2",
+               .led_bit = BMC_BIT_LED_USER2,
+       }
+};
+
+static DEFINE_MUTEX(led_lock);
+
+static void
+menf21bmc_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+       int led_val;
+       struct menf21bmc_led *led = container_of(led_cdev,
+                                       struct menf21bmc_led, cdev);
+
+       mutex_lock(&led_lock);
+       led_val = i2c_smbus_read_byte_data(led->i2c_client,
+                                          BMC_CMD_LED_GET_SET);
+       if (led_val < 0)
+               goto err_out;
+
+       if (value == LED_OFF)
+               led_val &= ~led->led_bit;
+       else
+               led_val |= led->led_bit;
+
+       i2c_smbus_write_byte_data(led->i2c_client,
+                                 BMC_CMD_LED_GET_SET, led_val);
+err_out:
+       mutex_unlock(&led_lock);
+}
+
+static int menf21bmc_led_probe(struct platform_device *pdev)
+{
+       int i;
+       int ret;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+
+       for (i = 0; i < ARRAY_SIZE(leds); i++) {
+               leds[i].cdev.name = leds[i].name;
+               leds[i].cdev.brightness_set = menf21bmc_led_set;
+               leds[i].i2c_client = i2c_client;
+               ret = led_classdev_register(&pdev->dev, &leds[i].cdev);
+               if (ret < 0)
+                       goto err_free_leds;
+       }
+       dev_info(&pdev->dev, "MEN 140F21P00 BMC LED device enabled\n");
+
+       return 0;
+
+err_free_leds:
+       dev_err(&pdev->dev, "failed to register LED device\n");
+
+       for (i = i - 1; i >= 0; i--)
+               led_classdev_unregister(&leds[i].cdev);
+
+       return ret;
+}
+
+static int menf21bmc_led_remove(struct platform_device *pdev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(leds); i++)
+               led_classdev_unregister(&leds[i].cdev);
+
+       return 0;
+}
+
+static struct platform_driver menf21bmc_led = {
+       .probe          = menf21bmc_led_probe,
+       .remove         = menf21bmc_led_remove,
+       .driver         = {
+               .name           = "menf21bmc_led",
+               .owner          = THIS_MODULE,
+       },
+};
+
+module_platform_driver(menf21bmc_led);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 14F021P00 BMC led driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_led");
index de5abf244746ac7dc32e99d02b61d131e4877a77..cf66ef1ffaf32d59b60ed2ff30873988ec2bd3e4 100644 (file)
@@ -454,6 +454,21 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
+config MFD_MENF21BMC
+       tristate "MEN 14F021P00 Board Management Controller Support"
+       depends on I2C
+       select MFD_CORE
+       help
+         Say yes here to add support for the MEN 14F021P00 BMC
+         which is a Board Management Controller connected to the I2C bus.
+         The device supports multiple sub-devices like LED, HWMON and WDT.
+         This driver provides common support for accessing the devices;
+         additional drivers must be enabled in order to use the
+         functionality of the BMC device.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc.
+
 config EZX_PCAP
        bool "Motorola EZXPCAP Support"
        depends on SPI_MASTER
index f00148782d9b3c234609ad10827430ec84c2a56b..d58068aa8aa9492fbbdd432123e4b5749d819bbf 100644 (file)
@@ -169,6 +169,7 @@ obj-$(CONFIG_MFD_AS3711)    += as3711.o
 obj-$(CONFIG_MFD_AS3722)       += as3722.o
 obj-$(CONFIG_MFD_STW481X)      += stw481x.o
 obj-$(CONFIG_MFD_IPAQ_MICRO)   += ipaq-micro.o
+obj-$(CONFIG_MFD_MENF21BMC)    += menf21bmc.o
 
 intel-soc-pmic-objs            := intel_soc_pmic_core.o intel_soc_pmic_crc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)   += intel-soc-pmic.o
index 4a5e885383f8a5a866d78545b6f59150a0d6cb2f..b8af263be5944efc9f2b8d17f32590be35d4c5e8 100644 (file)
 #include <linux/mfd/max14577.h>
 #include <linux/mfd/max14577-private.h>
 
+/*
+ * Table of valid charger currents for different Maxim chipsets.
+ * It is placed here because it is used by both charger and regulator driver.
+ */
+const struct maxim_charger_current maxim_charger_currents[] = {
+       [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
+       [MAXIM_DEVICE_TYPE_MAX14577] = {
+               .min            = MAX14577_CHARGER_CURRENT_LIMIT_MIN,
+               .high_start     = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START,
+               .high_step      = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP,
+               .max            = MAX14577_CHARGER_CURRENT_LIMIT_MAX,
+       },
+       [MAXIM_DEVICE_TYPE_MAX77836] = {
+               .min            = MAX77836_CHARGER_CURRENT_LIMIT_MIN,
+               .high_start     = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START,
+               .high_step      = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP,
+               .max            = MAX77836_CHARGER_CURRENT_LIMIT_MAX,
+       },
+};
+EXPORT_SYMBOL_GPL(maxim_charger_currents);
+
+/*
+ * maxim_charger_calc_reg_current - Calculate register value for current
+ * @limits:    constraints for charger, matching the MBCICHWRC register
+ * @min_ua:    minimal requested current, micro Amps
+ * @max_ua:    maximum requested current, micro Amps
+ * @dst:       destination to store calculated register value
+ *
+ * Calculates the value of MBCICHWRC (Fast Battery Charge Current) register
+ * for given current and stores it under pointed 'dst'. The stored value
+ * combines low bit (MBCICHWRCL) and high bits (MBCICHWRCH). It is also
+ * properly shifted.
+ *
+ * The calculated register value matches the current which:
+ *  - is always between <limits.min, limits.max>;
+ *  - is always less or equal to max_ua;
+ *  - is the highest possible value;
+ *  - may be lower than min_ua.
+ *
+ * On success returns 0. On error returns -EINVAL (requested min/max current
+ * is outside of given charger limits) and 'dst' is not set.
+ */
+int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
+               unsigned int min_ua, unsigned int max_ua, u8 *dst)
+{
+       unsigned int current_bits = 0xf;
+
+       if (min_ua > max_ua)
+               return -EINVAL;
+
+       if (min_ua > limits->max || max_ua < limits->min)
+               return -EINVAL;
+
+       if (max_ua < limits->high_start) {
+               /*
+                * Less than high_start, so set the minimal current
+                * (turn Low Bit off, 0 as high bits).
+                */
+               *dst = 0x0;
+               return 0;
+       }
+
+       /* max_ua is in range: <high_start, infinite>, cut it to limits.max */
+       max_ua = min(limits->max, max_ua);
+       max_ua -= limits->high_start;
+       /*
+        * There is no risk of overflow 'max_ua' here because:
+        *  - max_ua >= limits.high_start
+        *  - BUILD_BUG checks that 'limits' are: max >= high_start + high_step
+        */
+       current_bits = max_ua / limits->high_step;
+
+       /* Turn Low Bit on (use range <limits.high_start, limits.max>) ... */
+       *dst = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
+       /* and set proper High Bits */
+       *dst |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(maxim_charger_calc_reg_current);
+
 static const struct mfd_cell max14577_devs[] = {
        {
                .name = "max14577-muic",
@@ -35,7 +116,10 @@ static const struct mfd_cell max14577_devs[] = {
                .name = "max14577-regulator",
                .of_compatible = "maxim,max14577-regulator",
        },
-       { .name = "max14577-charger", },
+       {
+               .name = "max14577-charger",
+               .of_compatible = "maxim,max14577-charger",
+       },
 };
 
 static const struct mfd_cell max77836_devs[] = {
@@ -463,6 +547,20 @@ static int __init max14577_i2c_init(void)
        BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
        BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
 
+       /* Valid charger current values must be provided for each chipset */
+       BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
+
+       /* Check for valid values for charger */
+       BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START +
+                       MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
+                       MAX14577_CHARGER_CURRENT_LIMIT_MAX);
+       BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
+
+       BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START +
+                       MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
+                       MAX77836_CHARGER_CURRENT_LIMIT_MAX);
+       BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
+
        return i2c_add_driver(&max14577_i2c_driver);
 }
 subsys_initcall(max14577_i2c_init);
diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c
new file mode 100644 (file)
index 0000000..1c27434
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+
+#define BMC_CMD_WDT_EXIT_PROD  0x18
+#define BMC_CMD_WDT_PROD_STAT  0x19
+#define BMC_CMD_REV_MAJOR      0x80
+#define BMC_CMD_REV_MINOR      0x81
+#define BMC_CMD_REV_MAIN       0x82
+
+static struct mfd_cell menf21bmc_cell[] = {
+       { .name = "menf21bmc_wdt", },
+       { .name = "menf21bmc_led", },
+       { .name = "menf21bmc_hwmon", }
+};
+
+static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client)
+{
+       int val, ret;
+
+       val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT);
+       if (val < 0)
+               return val;
+
+       /*
+        * Production mode should be not active after delivery of the Board.
+        * To be sure we check it, inform the user and exit the mode
+        * if active.
+        */
+       if (val == 0x00) {
+               dev_info(&client->dev,
+                       "BMC in production mode. Exit production mode\n");
+
+               ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids)
+{
+       int rev_major, rev_minor, rev_main;
+       int ret;
+
+       ret = i2c_check_functionality(client->adapter,
+                                     I2C_FUNC_SMBUS_BYTE_DATA |
+                                     I2C_FUNC_SMBUS_WORD_DATA |
+                                     I2C_FUNC_SMBUS_BYTE);
+       if (!ret)
+               return -ENODEV;
+
+       rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR);
+       if (rev_major < 0) {
+               dev_err(&client->dev, "failed to get BMC major revision\n");
+               return rev_major;
+       }
+
+       rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR);
+       if (rev_minor < 0) {
+               dev_err(&client->dev, "failed to get BMC minor revision\n");
+               return rev_minor;
+       }
+
+       rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN);
+       if (rev_main < 0) {
+               dev_err(&client->dev, "failed to get BMC main revision\n");
+               return rev_main;
+       }
+
+       dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n",
+                rev_major, rev_minor, rev_main);
+
+       /*
+        * We have to exit the Production Mode of the BMC to activate the
+        * Watchdog functionality and the BIOS life sign monitoring.
+        */
+       ret = menf21bmc_wdt_exit_prod_mode(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to leave production mode\n");
+               return ret;
+       }
+
+       ret = mfd_add_devices(&client->dev, 0, menf21bmc_cell,
+                             ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to add BMC sub-devices\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int menf21bmc_remove(struct i2c_client *client)
+{
+       mfd_remove_devices(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id menf21bmc_id_table[] = {
+       { "menf21bmc" },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table);
+
+static struct i2c_driver menf21bmc_driver = {
+       .driver.name    = "menf21bmc",
+       .id_table       = menf21bmc_id_table,
+       .probe          = menf21bmc_probe,
+       .remove         = menf21bmc_remove,
+};
+
+module_i2c_driver(menf21bmc_driver);
+
+MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver");
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_LICENSE("GPL v2");
index 73cfcdf28a360e02a93e6a91ef47f5b54810363c..04e1d2fe220136a16e64113d3eacd4cdf4a5f490 100644 (file)
@@ -325,11 +325,12 @@ config CHARGER_MANAGER
           with help of suspend_again support.
 
 config CHARGER_MAX14577
-       tristate "Maxim MAX14577 MUIC battery charger driver"
+       tristate "Maxim MAX14577/77836 battery charger driver"
        depends on MFD_MAX14577
+       select SYSFS
        help
          Say Y to enable support for the battery charger control sysfs and
-         platform data of MAX14577 MUICs.
+         platform data of MAX14577/77836 MUICs.
 
 config CHARGER_MAX8997
        tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
index fad2a75b3604166b839385286c1e8478256bdd6e..0a2bc727702699f6977199940326838e8ccdbcad 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Battery charger driver for the Maxim 14577
+ * max14577_charger.c - Battery charger driver for the Maxim 14577/77836
  *
- * Copyright (C) 2013 Samsung Electronics
+ * Copyright (C) 2013,2014 Samsung Electronics
  * Krzysztof Kozlowski <k.kozlowski@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
 #include <linux/mfd/max14577-private.h>
+#include <linux/mfd/max14577.h>
 
 struct max14577_charger {
        struct device *dev;
        struct max14577 *max14577;
        struct power_supply     charger;
 
-       unsigned int    charging_state;
-       unsigned int    battery_state;
+       unsigned int            charging_state;
+       unsigned int            battery_state;
+
+       struct max14577_charger_platform_data   *pdata;
 };
 
+/*
+ * Helper function for mapping values of STATUS2/CHGTYP register on max14577
+ * and max77836 chipsets to enum maxim_muic_charger_type.
+ */
+static enum max14577_muic_charger_type maxim_get_charger_type(
+               enum maxim_device_type dev_type, u8 val) {
+       switch (val) {
+       case MAX14577_CHARGER_TYPE_NONE:
+       case MAX14577_CHARGER_TYPE_USB:
+       case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
+       case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
+       case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
+       case MAX14577_CHARGER_TYPE_SPECIAL_1A:
+               return val;
+       case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+       case MAX14577_CHARGER_TYPE_RESERVED:
+               if (dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+                       val |= 0x8;
+               return val;
+       default:
+               WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val);
+               return val;
+       }
+}
+
 static int max14577_get_charger_state(struct max14577_charger *chg)
 {
        struct regmap *rmap = chg->max14577->regmap;
@@ -89,19 +117,23 @@ static int max14577_get_online(struct max14577_charger *chg)
 {
        struct regmap *rmap = chg->max14577->regmap;
        u8 reg_data;
+       enum max14577_muic_charger_type chg_type;
 
        max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
        reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
-       switch (reg_data) {
+       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+       switch (chg_type) {
        case MAX14577_CHARGER_TYPE_USB:
        case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
        case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
        case MAX14577_CHARGER_TYPE_SPECIAL_1A:
        case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+       case MAX77836_CHARGER_TYPE_SPECIAL_BIAS:
                return 1;
        case MAX14577_CHARGER_TYPE_NONE:
        case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
        case MAX14577_CHARGER_TYPE_RESERVED:
+       case MAX77836_CHARGER_TYPE_RESERVED:
        default:
                return 0;
        }
@@ -118,10 +150,12 @@ static int max14577_get_battery_health(struct max14577_charger *chg)
        struct regmap *rmap = chg->max14577->regmap;
        int state = POWER_SUPPLY_HEALTH_GOOD;
        u8 reg_data;
+       enum max14577_muic_charger_type chg_type;
 
        max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
        reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
-       if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
+       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+       if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
                state = POWER_SUPPLY_HEALTH_DEAD;
                goto state_set;
        }
@@ -147,15 +181,131 @@ static int max14577_get_present(struct max14577_charger *chg)
        return 1;
 }
 
+static int max14577_set_fast_charge_timer(struct max14577_charger *chg,
+               unsigned long hours)
+{
+       u8 reg_data;
+
+       switch (hours) {
+       case 5 ... 7:
+               reg_data = hours - 3;
+               break;
+       case 0:
+               /* Disable */
+               reg_data = 0x7;
+               break;
+       default:
+               dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n",
+                               hours);
+               return -EINVAL;
+       }
+       reg_data <<= CHGCTRL1_TCHW_SHIFT;
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data);
+}
+
+static int max14577_init_constant_voltage(struct max14577_charger *chg,
+               unsigned int uvolt)
+{
+       u8 reg_data;
+
+       if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN ||
+                       uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+               return -EINVAL;
+
+       if (uvolt == 4200000)
+               reg_data = 0x0;
+       else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+               reg_data = 0x1f;
+       else if (uvolt <= 4280000) {
+               unsigned int val = uvolt;
+
+               val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN;
+               val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP;
+               if (uvolt <= 4180000)
+                       reg_data = 0x1 + val;
+               else
+                       reg_data = val; /* Fix for gap between 4.18V and 4.22V */
+       } else
+               return -EINVAL;
+
+       reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT;
+
+       return max14577_write_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL3, reg_data);
+}
+
+static int max14577_init_eoc(struct max14577_charger *chg,
+               unsigned int uamp)
+{
+       unsigned int current_bits = 0xf;
+       u8 reg_data;
+
+       switch (chg->max14577->dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               if (uamp < 5000)
+                       return -EINVAL; /* Requested current is too low */
+
+               if (uamp >= 7500 && uamp < 10000)
+                       current_bits = 0x0;
+               else if (uamp <= 50000) {
+                       /* <5000, 7499> and <10000, 50000> */
+                       current_bits = uamp / 5000;
+               } else {
+                       uamp = min(uamp, 100000U) - 50000U;
+                       current_bits = 0xa + uamp / 10000;
+               }
+               break;
+
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN)
+                       return -EINVAL; /* Requested current is too low */
+
+               uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
+               uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN;
+               current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP;
+               break;
+       }
+
+       reg_data = current_bits << CHGCTRL5_EOCS_SHIFT;
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK,
+                       reg_data);
+}
+
+static int max14577_init_fast_charge(struct max14577_charger *chg,
+               unsigned int uamp)
+{
+       u8 reg_data;
+       int ret;
+       const struct maxim_charger_current *limits =
+               &maxim_charger_currents[chg->max14577->dev_type];
+
+       ret = maxim_charger_calc_reg_current(limits, uamp, uamp, &reg_data);
+       if (ret) {
+               dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp);
+               return ret;
+       }
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL4,
+                       CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
+                       reg_data);
+}
+
 /*
  * Sets charger registers to proper and safe default values.
  * Some of these values are equal to defaults in MAX14577E
  * data sheet but there are minor differences.
  */
-static void max14577_charger_reg_init(struct max14577_charger *chg)
+static int max14577_charger_reg_init(struct max14577_charger *chg)
 {
        struct regmap *rmap = chg->max14577->regmap;
        u8 reg_data;
+       int ret;
 
        /*
         * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
@@ -167,10 +317,6 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
                        CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
                        reg_data);
 
-       /* Battery Fast-Charge Timer, from SM-V700: 6hrs */
-       reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data);
-
        /*
         * Wall-Adapter Rapid Charge, default on
         * Battery-Charger, default on
@@ -179,29 +325,46 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
        reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
        max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
 
-       /* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */
-       reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data);
-
-       /*
-        * Fast Battery-Charge Current Low, default 200-950mA
-        * Fast Battery-Charge Current High, from SM-V700: 450mA
-        */
-       reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
-       reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data);
-
-       /* End-of-Charge Current, from SM-V700: 50mA */
-       reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data);
-
        /* Auto Charging Stop, default off */
        reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
        max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
 
-       /* Overvoltage-Protection Threshold, from SM-V700: 6.5V */
-       reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT;
+       ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt);
+       if (ret)
+               return ret;
+
+       ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp);
+       if (ret)
+               return ret;
+
+       ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp);
+       if (ret)
+               return ret;
+
+       ret = max14577_set_fast_charge_timer(chg,
+                       MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT);
+       if (ret)
+               return ret;
+
+       /* Initialize Overvoltage-Protection Threshold */
+       switch (chg->pdata->ovp_uvolt) {
+       case 7500000:
+               reg_data = 0x0;
+               break;
+       case 6000000:
+       case 6500000:
+       case 7000000:
+               reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000;
+               break;
+       default:
+               dev_err(chg->dev, "Wrong value for OVP: %u\n",
+                               chg->pdata->ovp_uvolt);
+               return -EINVAL;
+       }
+       reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT;
        max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
+
+       return 0;
 }
 
 /* Support property from charger */
@@ -215,7 +378,11 @@ static enum power_supply_property max14577_charger_props[] = {
        POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
-static const char *model_name = "MAX14577";
+static const char * const model_names[] = {
+       [MAXIM_DEVICE_TYPE_UNKNOWN]     = "MAX14577-like",
+       [MAXIM_DEVICE_TYPE_MAX14577]    = "MAX14577",
+       [MAXIM_DEVICE_TYPE_MAX77836]    = "MAX77836",
+};
 static const char *manufacturer = "Maxim Integrated";
 
 static int max14577_charger_get_property(struct power_supply *psy,
@@ -244,7 +411,8 @@ static int max14577_charger_get_property(struct power_supply *psy,
                val->intval = max14577_get_online(chg);
                break;
        case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = model_name;
+               BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM);
+               val->strval = model_names[chg->max14577->dev_type];
                break;
        case POWER_SUPPLY_PROP_MANUFACTURER:
                val->strval = manufacturer;
@@ -256,6 +424,110 @@ static int max14577_charger_get_property(struct power_supply *psy,
        return ret;
 }
 
+#ifdef CONFIG_OF
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+               struct platform_device *pdev)
+{
+       struct max14577_charger_platform_data *pdata;
+       struct device_node *np = pdev->dev.of_node;
+       int ret;
+
+       if (!np) {
+               dev_err(&pdev->dev, "No charger OF node\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       ret = of_property_read_u32(np, "maxim,constant-uvolt",
+                       &pdata->constant_uvolt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,fast-charge-uamp",
+                       &pdata->fast_charge_uamp);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       return pdata;
+}
+#else /* CONFIG_OF */
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+               struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif /* CONFIG_OF */
+
+static ssize_t show_fast_charge_timer(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct max14577_charger *chg = dev_get_drvdata(dev);
+       u8 reg_data;
+       int ret;
+       unsigned int val;
+
+       ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1,
+                       &reg_data);
+       if (ret)
+               return ret;
+
+       reg_data &= CHGCTRL1_TCHW_MASK;
+       reg_data >>= CHGCTRL1_TCHW_SHIFT;
+       switch (reg_data) {
+       case 0x2 ... 0x4:
+               val = reg_data + 3;
+               break;
+       case 0x7:
+               val = 0;
+               break;
+       default:
+               val = 5;
+               break;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_fast_charge_timer(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct max14577_charger *chg = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       ret = max14577_set_fast_charge_timer(chg, val);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR,
+               show_fast_charge_timer, store_fast_charge_timer);
+
 static int max14577_charger_probe(struct platform_device *pdev)
 {
        struct max14577_charger *chg;
@@ -270,7 +542,13 @@ static int max14577_charger_probe(struct platform_device *pdev)
        chg->dev = &pdev->dev;
        chg->max14577 = max14577;
 
-       max14577_charger_reg_init(chg);
+       chg->pdata = max14577_charger_dt_init(pdev);
+       if (IS_ERR_OR_NULL(chg->pdata))
+               return PTR_ERR(chg->pdata);
+
+       ret = max14577_charger_reg_init(chg);
+       if (ret)
+               return ret;
 
        chg->charger.name = "max14577-charger",
        chg->charger.type = POWER_SUPPLY_TYPE_BATTERY,
@@ -278,24 +556,47 @@ static int max14577_charger_probe(struct platform_device *pdev)
        chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props),
        chg->charger.get_property = max14577_charger_get_property,
 
+       ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: create sysfs entry\n");
+               return ret;
+       }
+
        ret = power_supply_register(&pdev->dev, &chg->charger);
        if (ret) {
                dev_err(&pdev->dev, "failed: power supply register\n");
-               return ret;
+               goto err;
        }
 
+       /* Check for valid values for charger */
+       BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN +
+                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf !=
+                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
        return 0;
+
+err:
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+
+       return ret;
 }
 
 static int max14577_charger_remove(struct platform_device *pdev)
 {
        struct max14577_charger *chg = platform_get_drvdata(pdev);
 
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
        power_supply_unregister(&chg->charger);
 
        return 0;
 }
 
+static const struct platform_device_id max14577_charger_id[] = {
+       { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
+       { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_charger_id);
+
 static struct platform_driver max14577_charger_driver = {
        .driver = {
                .owner  = THIS_MODULE,
@@ -303,9 +604,10 @@ static struct platform_driver max14577_charger_driver = {
        },
        .probe          = max14577_charger_probe,
        .remove         = max14577_charger_remove,
+       .id_table       = max14577_charger_id,
 };
 module_platform_driver(max14577_charger_driver);
 
 MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("MAXIM 14577 charger driver");
+MODULE_DESCRIPTION("Maxim 14577/77836 charger driver");
 MODULE_LICENSE("GPL");
index 0fbac861080dac5cd6c4f62dc59338444205a60a..14d44706327bca64fc487a4a057a4c2a0c130b1d 100644 (file)
@@ -277,7 +277,8 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
 #endif /* CONFIG_PM_SLEEP */
 
 static const struct i2c_device_id max17040_id[] = {
-       { "max17040", 0 },
+       { "max17040" },
+       { "max77836-battery" },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max17040_id);
index 5d9c605cf534d9dea23b99885b1a5babffc65869..0ff5a20ac95855a0cb4e0df80cd7f09508759a54 100644 (file)
 #include <linux/mfd/max14577-private.h>
 #include <linux/regulator/of_regulator.h>
 
-/*
- * Valid limits of current for max14577 and max77836 chargers.
- * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
- * register for given chipset.
- */
-struct maxim_charger_current {
-       /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
-       unsigned int min;
-       /*
-        * Minimal current when high setting is active,
-        * set in CHGCTRL4/MBCICHWRCH, uA
-        */
-       unsigned int high_start;
-       /* Value of one step in high setting, uA */
-       unsigned int high_step;
-       /* Maximum current of high setting, uA */
-       unsigned int max;
-};
-
-/* Table of valid charger currents for different Maxim chipsets */
-static const struct maxim_charger_current maxim_charger_currents[] = {
-       [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
-       [MAXIM_DEVICE_TYPE_MAX14577] = {
-               .min            = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
-               .high_start     = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
-               .high_step      = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
-               .max            = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
-       },
-       [MAXIM_DEVICE_TYPE_MAX77836] = {
-               .min            = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
-               .high_start     = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
-               .high_step      = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
-               .max            = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
-       },
-};
-
 static int max14577_reg_is_enabled(struct regulator_dev *rdev)
 {
        int rid = rdev_get_id(rdev);
@@ -103,8 +67,8 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
 static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
                int min_uA, int max_uA)
 {
-       int i, current_bits = 0xf;
        u8 reg_data;
+       int ret;
        struct max14577 *max14577 = rdev_get_drvdata(rdev);
        const struct maxim_charger_current *limits =
                &maxim_charger_currents[max14577->dev_type];
@@ -112,35 +76,9 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
        if (rdev_get_id(rdev) != MAX14577_CHARGER)
                return -EINVAL;
 
-       if (min_uA > limits->max || max_uA < limits->min)
-               return -EINVAL;
-
-       if (max_uA < limits->high_start) {
-               /*
-                * Less than high_start,
-                * so set the minimal current (turn only Low Bit off)
-                */
-               u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
-               return max14577_update_reg(rdev->regmap,
-                               MAX14577_CHG_REG_CHG_CTRL4,
-                               CHGCTRL4_MBCICHWRCL_MASK, reg_data);
-       }
-
-       /*
-        * max_uA is in range: <high_start, inifinite>, so search for
-        * valid current starting from maximum current.
-        */
-       for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
-               if (i <= max_uA)
-                       break;
-               current_bits--;
-       }
-       BUG_ON(current_bits < 0); /* Cannot happen */
-
-       /* Turn Low Bit on (use range high_start-max)... */
-       reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
-       /* and set proper High Bits */
-       reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
+       ret = maxim_charger_calc_reg_current(limits, min_uA, max_uA, &reg_data);
+       if (ret)
+               return ret;
 
        return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4,
                        CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
@@ -442,16 +380,6 @@ static struct platform_driver max14577_regulator_driver = {
 
 static int __init max14577_regulator_init(void)
 {
-       /* Check for valid values for charger */
-       BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
-                       MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
-                       MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
-       BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
-                       MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
-                       MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
-       /* Valid charger current values must be provided for each chipset */
-       BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
-
        BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
        BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
 
index f57312fced80f0e60a75019db688a25b659a9886..a50828e34e3b8d692db94530767a982a222fe404 100644 (file)
@@ -95,6 +95,16 @@ config GPIO_WATCHDOG
          If you say yes here you get support for watchdog device
          controlled through GPIO-line.
 
+config MENF21BMC_WATCHDOG
+       tristate "MEN 14F021P00 BMC Watchdog"
+       depends on MFD_MENF21BMC
+       select WATCHDOG_CORE
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC Watchdog.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc_wdt.
+
 config WM831X_WATCHDOG
        tristate "WM831x watchdog"
        depends on MFD_WM831X
index 468c3204c3b190b7be20616d02d25ba947408581..de1701470c14fd12f2bfcc08b74fd56cedbe3a6a 100644 (file)
@@ -178,3 +178,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+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 (file)
index 0000000..2042874
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#define DEVNAME "menf21bmc_wdt"
+
+#define BMC_CMD_WD_ON          0x11
+#define BMC_CMD_WD_OFF         0x12
+#define BMC_CMD_WD_TRIG                0x13
+#define BMC_CMD_WD_TIME                0x14
+#define BMC_CMD_WD_STATE       0x17
+#define BMC_WD_OFF_VAL         0x69
+#define BMC_CMD_RST_RSN                0x92
+
+#define BMC_WD_TIMEOUT_MIN     1       /* in sec */
+#define BMC_WD_TIMEOUT_MAX     6553    /* in sec */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct menf21bmc_wdt {
+       struct watchdog_device wdt;
+       struct i2c_client *i2c_client;
+};
+
+static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt *data)
+{
+       int rst_rsn;
+
+       rst_rsn = i2c_smbus_read_byte_data(data->i2c_client, BMC_CMD_RST_RSN);
+       if (rst_rsn < 0)
+               return rst_rsn;
+
+       if (rst_rsn == 0x02)
+               data->wdt.bootstatus |= WDIOF_CARDRESET;
+       else if (rst_rsn == 0x05)
+               data->wdt.bootstatus |= WDIOF_EXTERN1;
+       else if (rst_rsn == 0x06)
+               data->wdt.bootstatus |= WDIOF_EXTERN2;
+       else if (rst_rsn == 0x0A)
+               data->wdt.bootstatus |= WDIOF_POWERUNDER;
+
+       return 0;
+}
+
+static int menf21bmc_wdt_start(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_ON);
+}
+
+static int menf21bmc_wdt_stop(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte_data(drv_data->i2c_client,
+                                        BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
+}
+
+static int
+menf21bmc_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout)
+{
+       int ret;
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       /*
+        *  BMC Watchdog does have a resolution of 100ms.
+        *  Watchdog API defines the timeout in seconds, so we have to
+        *  multiply the value.
+        */
+       ret = i2c_smbus_write_word_data(drv_data->i2c_client,
+                                       BMC_CMD_WD_TIME, timeout * 10);
+       if (ret < 0)
+               return ret;
+
+       wdt->timeout = timeout;
+
+       return 0;
+}
+
+static int menf21bmc_wdt_ping(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_TRIG);
+}
+
+static const struct watchdog_info menf21bmc_wdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity = DEVNAME,
+};
+
+static const struct watchdog_ops menf21bmc_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = menf21bmc_wdt_start,
+       .stop           = menf21bmc_wdt_stop,
+       .ping           = menf21bmc_wdt_ping,
+       .set_timeout    = menf21bmc_wdt_settimeout,
+};
+
+static int menf21bmc_wdt_probe(struct platform_device *pdev)
+{
+       int ret, bmc_timeout;
+       struct menf21bmc_wdt *drv_data;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+
+       drv_data = devm_kzalloc(&pdev->dev,
+                               sizeof(struct menf21bmc_wdt), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->wdt.ops = &menf21bmc_wdt_ops;
+       drv_data->wdt.info = &menf21bmc_wdt_info;
+       drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
+       drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+       drv_data->i2c_client = i2c_client;
+
+       /*
+        * Get the current wdt timeout value from the BMC because
+        * the BMC will save the value set before if the system restarts.
+        */
+       bmc_timeout = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              BMC_CMD_WD_TIME);
+       if (bmc_timeout < 0) {
+               dev_err(&pdev->dev, "failed to get current WDT timeout\n");
+               return bmc_timeout;
+       }
+
+       watchdog_init_timeout(&drv_data->wdt, bmc_timeout / 10, &pdev->dev);
+       watchdog_set_nowayout(&drv_data->wdt, nowayout);
+       watchdog_set_drvdata(&drv_data->wdt, drv_data);
+       platform_set_drvdata(pdev, drv_data);
+
+       ret = menf21bmc_wdt_set_bootstatus(drv_data);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to set Watchdog bootstatus\n");
+               return ret;
+       }
+
+       ret = watchdog_register_device(&drv_data->wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register Watchdog device\n");
+               return ret;
+       }
+
+       dev_info(&pdev->dev, "MEN 14F021P00 BMC Watchdog device enabled\n");
+
+       return 0;
+}
+
+static int menf21bmc_wdt_remove(struct platform_device *pdev)
+{
+       struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev);
+
+       dev_warn(&pdev->dev,
+                "Unregister MEN 14F021P00 BMC Watchdog device, board may reset\n");
+
+       watchdog_unregister_device(&drv_data->wdt);
+
+       return 0;
+}
+
+static void menf21bmc_wdt_shutdown(struct platform_device *pdev)
+{
+       struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev);
+
+       i2c_smbus_write_word_data(drv_data->i2c_client,
+                                 BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
+}
+
+static struct  platform_driver menf21bmc_wdt = {
+       .driver         = {
+               .owner = THIS_MODULE,
+               .name   = DEVNAME,
+       },
+       .probe          = menf21bmc_wdt_probe,
+       .remove         = menf21bmc_wdt_remove,
+       .shutdown       = menf21bmc_wdt_shutdown,
+};
+
+module_platform_driver(menf21bmc_wdt);
+
+MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_wdt");
index 499253604026cf94044fe18709501b3b47e8fc3c..f01c1fae4d840ef09f3b8932aa0d07a2220e7e66 100644 (file)
@@ -72,15 +72,33 @@ enum max14577_muic_reg {
        MAX14577_MUIC_REG_END,
 };
 
+/*
+ * Combined charger types for max14577 and max77836.
+ *
+ * On max14577 three lower bits map to STATUS2/CHGTYP field.
+ * However the max77836 has different two last values of STATUS2/CHGTYP.
+ * To indicate the difference enum has two additional values for max77836.
+ * These values are just a register value bitwise OR with 0x8.
+ */
 enum max14577_muic_charger_type {
-       MAX14577_CHARGER_TYPE_NONE = 0,
-       MAX14577_CHARGER_TYPE_USB,
-       MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT,
-       MAX14577_CHARGER_TYPE_DEDICATED_CHG,
-       MAX14577_CHARGER_TYPE_SPECIAL_500MA,
-       MAX14577_CHARGER_TYPE_SPECIAL_1A,
-       MAX14577_CHARGER_TYPE_RESERVED,
-       MAX14577_CHARGER_TYPE_DEAD_BATTERY = 7,
+       MAX14577_CHARGER_TYPE_NONE              = 0x0,
+       MAX14577_CHARGER_TYPE_USB               = 0x1,
+       MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT   = 0x2,
+       MAX14577_CHARGER_TYPE_DEDICATED_CHG     = 0x3,
+       MAX14577_CHARGER_TYPE_SPECIAL_500MA     = 0x4,
+       /* Special 1A or 2A charger */
+       MAX14577_CHARGER_TYPE_SPECIAL_1A        = 0x5,
+       /* max14577: reserved, used on max77836 */
+       MAX14577_CHARGER_TYPE_RESERVED          = 0x6,
+       /* max14577: dead-battery charing with maximum current 100mA */
+       MAX14577_CHARGER_TYPE_DEAD_BATTERY      = 0x7,
+       /*
+        * max77836: special charger (bias on D+/D-),
+        * matches register value of 0x6
+        */
+       MAX77836_CHARGER_TYPE_SPECIAL_BIAS      = 0xe,
+       /* max77836: reserved, register value 0x7 */
+       MAX77836_CHARGER_TYPE_RESERVED          = 0xf,
 };
 
 /* MAX14577 interrupts */
@@ -121,13 +139,15 @@ enum max14577_muic_charger_type {
 #define STATUS2_CHGTYP_SHIFT           0
 #define STATUS2_CHGDETRUN_SHIFT                3
 #define STATUS2_DCDTMR_SHIFT           4
-#define STATUS2_DBCHG_SHIFT            5
+#define MAX14577_STATUS2_DBCHG_SHIFT   5
+#define MAX77836_STATUS2_DXOVP_SHIFT   5
 #define STATUS2_VBVOLT_SHIFT           6
 #define MAX77836_STATUS2_VIDRM_SHIFT   7
 #define STATUS2_CHGTYP_MASK            (0x7 << STATUS2_CHGTYP_SHIFT)
 #define STATUS2_CHGDETRUN_MASK         BIT(STATUS2_CHGDETRUN_SHIFT)
 #define STATUS2_DCDTMR_MASK            BIT(STATUS2_DCDTMR_SHIFT)
-#define STATUS2_DBCHG_MASK             BIT(STATUS2_DBCHG_SHIFT)
+#define MAX14577_STATUS2_DBCHG_MASK    BIT(MAX14577_STATUS2_DBCHG_SHIFT)
+#define MAX77836_STATUS2_DXOVP_MASK    BIT(MAX77836_STATUS2_DXOVP_SHIFT)
 #define STATUS2_VBVOLT_MASK            BIT(STATUS2_VBVOLT_SHIFT)
 #define MAX77836_STATUS2_VIDRM_MASK    BIT(MAX77836_STATUS2_VIDRM_SHIFT)
 
@@ -177,9 +197,11 @@ enum max14577_muic_charger_type {
 #define CTRL3_JIGSET_SHIFT             0
 #define CTRL3_BOOTSET_SHIFT            2
 #define CTRL3_ADCDBSET_SHIFT           4
+#define CTRL3_WBTH_SHIFT               6
 #define CTRL3_JIGSET_MASK              (0x3 << CTRL3_JIGSET_SHIFT)
 #define CTRL3_BOOTSET_MASK             (0x3 << CTRL3_BOOTSET_SHIFT)
 #define CTRL3_ADCDBSET_MASK            (0x3 << CTRL3_ADCDBSET_SHIFT)
+#define CTRL3_WBTH_MASK                        (0x3 << CTRL3_WBTH_SHIFT)
 
 /* Slave addr = 0x4A: Charger */
 enum max14577_charger_reg {
@@ -210,16 +232,20 @@ enum max14577_charger_reg {
 #define CDETCTRL1_CHGTYPMAN_SHIFT      1
 #define CDETCTRL1_DCDEN_SHIFT          2
 #define CDETCTRL1_DCD2SCT_SHIFT                3
-#define CDETCTRL1_DCHKTM_SHIFT         4
-#define CDETCTRL1_DBEXIT_SHIFT         5
+#define MAX14577_CDETCTRL1_DCHKTM_SHIFT        4
+#define MAX77836_CDETCTRL1_CDLY_SHIFT  4
+#define MAX14577_CDETCTRL1_DBEXIT_SHIFT        5
+#define MAX77836_CDETCTRL1_DCDCPL_SHIFT        5
 #define CDETCTRL1_DBIDLE_SHIFT         6
 #define CDETCTRL1_CDPDET_SHIFT         7
 #define CDETCTRL1_CHGDETEN_MASK                BIT(CDETCTRL1_CHGDETEN_SHIFT)
 #define CDETCTRL1_CHGTYPMAN_MASK       BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
 #define CDETCTRL1_DCDEN_MASK           BIT(CDETCTRL1_DCDEN_SHIFT)
 #define CDETCTRL1_DCD2SCT_MASK         BIT(CDETCTRL1_DCD2SCT_SHIFT)
-#define CDETCTRL1_DCHKTM_MASK          BIT(CDETCTRL1_DCHKTM_SHIFT)
-#define CDETCTRL1_DBEXIT_MASK          BIT(CDETCTRL1_DBEXIT_SHIFT)
+#define MAX14577_CDETCTRL1_DCHKTM_MASK BIT(MAX14577_CDETCTRL1_DCHKTM_SHIFT)
+#define MAX77836_CDETCTRL1_CDDLY_MASK  BIT(MAX77836_CDETCTRL1_CDDLY_SHIFT)
+#define MAX14577_CDETCTRL1_DBEXIT_MASK BIT(MAX14577_CDETCTRL1_DBEXIT_SHIFT)
+#define MAX77836_CDETCTRL1_DCDCPL_MASK BIT(MAX77836_CDETCTRL1_DCDCPL_SHIFT)
 #define CDETCTRL1_DBIDLE_MASK          BIT(CDETCTRL1_DBIDLE_SHIFT)
 #define CDETCTRL1_CDPDET_MASK          BIT(CDETCTRL1_CDPDET_SHIFT)
 
@@ -255,17 +281,36 @@ enum max14577_charger_reg {
 #define CHGCTRL7_OTPCGHCVS_SHIFT       0
 #define CHGCTRL7_OTPCGHCVS_MASK                (0x3 << CHGCTRL7_OTPCGHCVS_SHIFT)
 
-/* MAX14577 regulator current limits (as in CHGCTRL4 register), uA */
-#define MAX14577_REGULATOR_CURRENT_LIMIT_MIN            90000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START    200000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP      50000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX           950000
-
-/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
-#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN            45000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START    100000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP      25000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX           475000
+/* MAX14577 charger current limits (as in CHGCTRL4 register), uA */
+#define MAX14577_CHARGER_CURRENT_LIMIT_MIN              90000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START      200000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP        50000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_MAX             950000U
+
+/* MAX77836 charger current limits (as in CHGCTRL4 register), uA */
+#define MAX77836_CHARGER_CURRENT_LIMIT_MIN              45000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START      100000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP        25000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_MAX             475000U
+
+/*
+ * MAX14577 charger End-Of-Charge current limits
+ * (as in CHGCTRL5 register), uA
+ */
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN         50000U
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP                10000U
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX         200000U
+
+/*
+ * MAX14577/MAX77836 Battery Constant Voltage
+ * (as in CHGCTRL3 register), uV
+ */
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN             4000000U
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP            20000U
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX             4350000U
+
+/* Default value for fast charge timer, in hours */
+#define MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT                5
 
 /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
 #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE             4900000
index c83fbed1c7b64a7e59ab0ab36e8cfa8fb4d1b6af..ccfaf952c31ba65645235a945c74470369ca6cd1 100644 (file)
@@ -54,6 +54,13 @@ struct max14577_regulator_platform_data {
        struct device_node *of_node;
 };
 
+struct max14577_charger_platform_data {
+       u32 constant_uvolt;
+       u32 fast_charge_uamp;
+       u32 eoc_uamp;
+       u32 ovp_uvolt;
+};
+
 /*
  * MAX14577 MFD platform data
  */
@@ -74,4 +81,27 @@ struct max14577_platform_data {
        struct max14577_regulator_platform_data *regulators;
 };
 
+/*
+ * Valid limits of current for max14577 and max77836 chargers.
+ * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
+ * register for given chipset.
+ */
+struct maxim_charger_current {
+       /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
+       unsigned int min;
+       /*
+        * Minimal current when high setting is active,
+        * set in CHGCTRL4/MBCICHWRCH, uA
+        */
+       unsigned int high_start;
+       /* Value of one step in high setting, uA */
+       unsigned int high_step;
+       /* Maximum current of high setting, uA */
+       unsigned int max;
+};
+
+extern const struct maxim_charger_current maxim_charger_currents[];
+extern int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
+               unsigned int min_ua, unsigned int max_ua, u8 *dst);
+
 #endif /* __MAX14577_H__ */