Merge branches 'ib-mfd-arm-3.18', 'ib-mfd-hwmon-leds-watchdog-3.18' and 'ib-mfd-power-charger-regulator-3.18' into ibs-for-mfd-merged
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
index 78c7bac..ad4fb90 100644
--- a/Documentation/ABI/testing/sysfs-class-power
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -18,3 +18,17 @@
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
index 0000000..236264c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max14577.txt
@@ -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
index 0000000..2a273a0
--- /dev/null
+++ b/Documentation/hwmon/menf21bmc
@@ -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"
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f00d048..c08d172 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -839,6 +839,16 @@
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
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index be28152..c90a761 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -115,6 +115,7 @@
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
index 0000000..c92229d
--- /dev/null
+++ b/drivers/hwmon/menf21bmc_hwmon.c
@@ -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");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 8c96e2d..eadd56c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -468,6 +468,15 @@
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
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index d8cc5f2..9a72fdd 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@
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
index 0000000..89dd577
--- /dev/null
+++ b/drivers/leds/leds-menf21bmc.c
@@ -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");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de5abf2..cf66ef1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -454,6 +454,21 @@
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
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f001487..d58068a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -169,6 +169,7 @@
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
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 4a5e885..b8af263 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -26,6 +26,87 @@
#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 @@
.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 @@
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
index 0000000..1c27434
--- /dev/null
+++ b/drivers/mfd/menf21bmc.c
@@ -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");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 73cfcdf..04e1d2f 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -325,11 +325,12 @@
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"
diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c
index fad2a75..0a2bc72 100644
--- a/drivers/power/max14577_charger.c
+++ b/drivers/power/max14577_charger.c
@@ -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
@@ -19,16 +19,44 @@
#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 @@
{
struct regmap *rmap = chg->max14577->regmap;
u8 reg_data;
+ enum max14577_muic_charger_type chg_type;
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_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 @@
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, ®_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 @@
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, ®_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 @@
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 @@
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 @@
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 @@
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 @@
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,
+ ®_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 @@
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 @@
chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props),
chg->charger.get_property = max14577_charger_get_property,
- ret = power_supply_register(&pdev->dev, &chg->charger);
+ ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
if (ret) {
- dev_err(&pdev->dev, "failed: power supply register\n");
+ 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");
+ 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 @@
},
.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");
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 0fbac86..14d4470 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -277,7 +277,8 @@
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id max17040_id[] = {
- { "max17040", 0 },
+ { "max17040" },
+ { "max77836-battery" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17040_id);
diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c
index 5d9c605..0ff5a20 100644
--- a/drivers/regulator/max14577.c
+++ b/drivers/regulator/max14577.c
@@ -22,42 +22,6 @@
#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_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 @@
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, ®_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 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);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..a50828e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -95,6 +95,16 @@
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
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..de17014 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -178,3 +178,4 @@
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
index 0000000..2042874
--- /dev/null
+++ b/drivers/watchdog/menf21bmc_wdt.c
@@ -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");
diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h
index 4992536..f01c1fa 100644
--- a/include/linux/mfd/max14577-private.h
+++ b/include/linux/mfd/max14577-private.h
@@ -72,15 +72,33 @@
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 @@
#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 @@
#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 @@
#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 @@
#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
+/* 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 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
+/* 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
diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h
index c83fbed..ccfaf95 100644
--- a/include/linux/mfd/max14577.h
+++ b/include/linux/mfd/max14577.h
@@ -54,6 +54,13 @@
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_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__ */