Merge tag 'for-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 02:49:09 +0000 (19:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 02:49:09 +0000 (19:49 -0700)
Pull power supply and reset updates from Sebastian Reichel:
 - introduce reboot mode driver
 - add DT support to max8903
 - add power supply support for axp221
 - misc fixes

* tag 'for-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply:
  power: reset: add reboot mode driver
  dt-bindings: power: reset: add document for reboot-mode driver
  power_supply: fix return value of get_property
  power: qcom_smbb: Make an extcon for usb cable detection
  max8903: adds support for initiation via device tree
  max8903: adds documentation for device tree bindings.
  max8903: remove unnecessary 'out of memory' error message.
  max8903: removes non zero validity checks on gpios.
  max8903: adds requesting of gpios.
  max8903: cleans up confusing relationship between dc_valid, dok and dcm.
  max8903: store pointer to pdata instead of copying it.
  power_supply: bq27xxx_battery: Group register mappings into one table
  docs: Move brcm,bcm21664-resetmgr.txt
  power/reset: make syscon_poweroff() static
  power: axp20x_usb: Add support for usb power-supply on axp22x pmics
  power_supply: bq27xxx_battery: Index register numbers by enum
  power_supply: bq27xxx_battery: Fix copy/paste error in header comment
  MAINTAINERS: Add file patterns for power supply device tree bindings
  power: reset: keystone: Enable COMPILE_TEST

23 files changed:
Documentation/devicetree/bindings/power/max8903-charger.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/reset/brcm,bcm21664-resetmgr.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/reset/reboot-mode.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt
Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt [deleted file]
MAINTAINERS
drivers/power/Kconfig
drivers/power/axp20x_usb_power.c
drivers/power/bq27xxx_battery.c
drivers/power/bq27xxx_battery_i2c.c
drivers/power/max8903_charger.c
drivers/power/power_supply_core.c
drivers/power/power_supply_sysfs.c
drivers/power/qcom_smbb.c
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/reboot-mode.c [new file with mode: 0644]
drivers/power/reset/reboot-mode.h [new file with mode: 0644]
drivers/power/reset/syscon-poweroff.c
drivers/power/reset/syscon-reboot-mode.c [new file with mode: 0644]
include/linux/power/max8903_charger.h
include/linux/power_supply.h

diff --git a/Documentation/devicetree/bindings/power/max8903-charger.txt b/Documentation/devicetree/bindings/power/max8903-charger.txt
new file mode 100644 (file)
index 0000000..f0f4e12
--- /dev/null
@@ -0,0 +1,25 @@
+Maxim Semiconductor MAX8903 Battery Charger bindings
+
+Required properties:
+- compatible: "maxim,max8903" for MAX8903 Battery Charger
+- dok-gpios: Valid DC power has been detected (active low, input), optional if uok-gpios is provided
+- uok-gpios: Valid USB power has been detected (active low, input), optional if dok-gpios is provided
+
+Optional properties:
+- cen-gpios: Charge enable pin (active low, output)
+- chg-gpios: Charger status pin (active low, input)
+- flt-gpios: Fault pin (active low, output)
+- dcm-gpios: Current limit mode setting (DC=1 or USB=0, output)
+- usus-gpios: USB suspend pin (active high, output)
+
+
+Example:
+
+       max8903-charger {
+               compatible = "maxim,max8903";
+               dok-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+               flt-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
+               chg-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
+               cen-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+               status = "okay";
+       };
diff --git a/Documentation/devicetree/bindings/power/reset/brcm,bcm21664-resetmgr.txt b/Documentation/devicetree/bindings/power/reset/brcm,bcm21664-resetmgr.txt
new file mode 100644 (file)
index 0000000..93f31ca
--- /dev/null
@@ -0,0 +1,14 @@
+Broadcom Kona Family Reset Manager
+----------------------------------
+
+The reset manager is used on the Broadcom BCM21664 SoC.
+
+Required properties:
+  - compatible: brcm,bcm21664-resetmgr
+  - reg: memory address & range
+
+Example:
+       brcm,resetmgr@35001f00 {
+               compatible = "brcm,bcm21664-resetmgr";
+               reg = <0x35001f00 0x24>;
+       };
diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
new file mode 100644 (file)
index 0000000..de34f27
--- /dev/null
@@ -0,0 +1,25 @@
+Generic reboot mode core map driver
+
+This driver get reboot mode arguments and call the write
+interface to store the magic value in special register
+or ram. Then the bootloader can read it and take different
+action according to the argument stored.
+
+All mode properties are vendor specific, it is a indication to tell
+the bootloader what to do when the system reboots, and should be named
+as mode-xxx = <magic> (xxx is mode name, magic should be a none-zero value).
+
+For example modes common on Android platform:
+- mode-normal: Normal reboot mode, system reboot with command "reboot".
+- mode-recovery: Android Recovery mode, it is a mode to format the device or update a new image.
+- mode-bootloader: Android fastboot mode, it's a mode to re-flash partitions on the Android based device.
+- mode-loader: A bootloader mode, it's a mode used to download image on Rockchip platform,
+              usually used in development.
+
+Example:
+       reboot-mode {
+               mode-normal = <BOOT_NORMAL>;
+               mode-recovery = <BOOT_RECOVERY>;
+               mode-bootloader = <BOOT_FASTBOOT>;
+               mode-loader = <BOOT_BL_DOWNLOAD>;
+       }
diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
new file mode 100644 (file)
index 0000000..f7ce1d8
--- /dev/null
@@ -0,0 +1,35 @@
+SYSCON reboot mode driver
+
+This driver gets reboot mode magic value form reboot-mode driver
+and stores it in a SYSCON mapped register. Then the bootloader
+can read it and take different action according to the magic
+value stored.
+
+This DT node should be represented as a sub-node of a "syscon", "simple-mfd"
+node.
+
+Required properties:
+- compatible: should be "syscon-reboot-mode"
+- offset: offset in the register map for the storage register (in bytes)
+
+Optional property:
+- mask: bits mask of the bits in the register to store the reboot mode magic value,
+  default set to 0xffffffff if missing.
+
+The rest of the properties should follow the generic reboot-mode description
+found in reboot-mode.txt
+
+Example:
+       pmu: pmu@20004000 {
+               compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd";
+               reg = <0x20004000 0x100>;
+
+               reboot-mode {
+                       compatible = "syscon-reboot-mode";
+                       offset = <0x40>;
+                       mode-normal = <BOOT_NORMAL>;
+                       mode-recovery = <BOOT_RECOVERY>;
+                       mode-bootloader = <BOOT_FASTBOOT>;
+                       mode-loader = <BOOT_BL_DOWNLOAD>;
+               };
+       };
index 862f4a4..f1d7bee 100644 (file)
@@ -1,7 +1,8 @@
 AXP20x USB power supply
 
 Required Properties:
--compatible: "x-powers,axp202-usb-power-supply"
+-compatible: One of: "x-powers,axp202-usb-power-supply"
+                     "x-powers,axp221-usb-power-supply"
 
 This node is a subnode of the axp20x PMIC.
 
diff --git a/Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt b/Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt
deleted file mode 100644 (file)
index 93f31ca..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Broadcom Kona Family Reset Manager
-----------------------------------
-
-The reset manager is used on the Broadcom BCM21664 SoC.
-
-Required properties:
-  - compatible: brcm,bcm21664-resetmgr
-  - reg: memory address & range
-
-Example:
-       brcm,resetmgr@35001f00 {
-               compatible = "brcm,bcm21664-resetmgr";
-               reg = <0x35001f00 0x24>;
-       };
index b407c5f..771c31c 100644 (file)
@@ -9169,6 +9169,8 @@ M:        David Woodhouse <dwmw2@infradead.org>
 L:     linux-pm@vger.kernel.org
 T:     git git://git.infradead.org/battery-2.6.git
 S:     Maintained
+F:     Documentation/devicetree/bindings/power/
+F:     Documentation/devicetree/bindings/power_supply/
 F:     include/linux/power_supply.h
 F:     drivers/power/
 X:     drivers/power/avs/
index 0f11a0f..acd4a15 100644 (file)
@@ -394,6 +394,7 @@ config CHARGER_QCOM_SMBB
        tristate "Qualcomm Switch-Mode Battery Charger and Boost"
        depends on MFD_SPMI_PMIC || COMPILE_TEST
        depends on OF
+       depends on EXTCON
        help
          Say Y to include support for the Switch-Mode Battery Charger and
          Boost (SMBB) hardware found in Qualcomm PM8941 PMICs.  The charger
index 421a90b..6af6feb 100644 (file)
@@ -42,6 +42,7 @@
 #define AXP20X_VBUS_MON_VBUS_VALID     BIT(3)
 
 struct axp20x_usb_power {
+       struct device_node *np;
        struct regmap *regmap;
        struct power_supply *supply;
 };
@@ -85,7 +86,12 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
 
                switch (v & AXP20X_VBUS_CLIMIT_MASK) {
                case AXP20X_VBUC_CLIMIT_100mA:
-                       val->intval = 100000;
+                       if (of_device_is_compatible(power->np,
+                                       "x-powers,axp202-usb-power-supply")) {
+                               val->intval = 100000;
+                       } else {
+                               val->intval = -1; /* No 100mA limit */
+                       }
                        break;
                case AXP20X_VBUC_CLIMIT_500mA:
                        val->intval = 500000;
@@ -122,16 +128,19 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
                        break;
                }
 
-               ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v);
-               if (ret)
-                       return ret;
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
 
-               if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) {
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-                       break;
-               }
+               if (of_device_is_compatible(power->np,
+                               "x-powers,axp202-usb-power-supply")) {
+                       ret = regmap_read(power->regmap,
+                                         AXP20X_USB_OTG_STATUS, &v);
+                       if (ret)
+                               return ret;
 
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+                       if (!(v & AXP20X_USB_STATUS_VBUS_VALID))
+                               val->intval =
+                                       POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               }
                break;
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
@@ -156,6 +165,14 @@ static enum power_supply_property axp20x_usb_power_properties[] = {
        POWER_SUPPLY_PROP_CURRENT_NOW,
 };
 
+static enum power_supply_property axp22x_usb_power_properties[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
 static const struct power_supply_desc axp20x_usb_power_desc = {
        .name = "axp20x-usb",
        .type = POWER_SUPPLY_TYPE_USB,
@@ -164,13 +181,25 @@ static const struct power_supply_desc axp20x_usb_power_desc = {
        .get_property = axp20x_usb_power_get_property,
 };
 
+static const struct power_supply_desc axp22x_usb_power_desc = {
+       .name = "axp20x-usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = axp22x_usb_power_properties,
+       .num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
+       .get_property = axp20x_usb_power_get_property,
+};
+
 static int axp20x_usb_power_probe(struct platform_device *pdev)
 {
        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
        struct power_supply_config psy_cfg = {};
        struct axp20x_usb_power *power;
-       static const char * const irq_names[] = { "VBUS_PLUGIN",
-               "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" };
+       static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN",
+               "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL };
+       static const char * const axp22x_irq_names[] = {
+               "VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
+       static const char * const *irq_names;
+       const struct power_supply_desc *usb_power_desc;
        int i, irq, ret;
 
        if (!of_device_is_available(pdev->dev.of_node))
@@ -185,31 +214,47 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
        if (!power)
                return -ENOMEM;
 
+       power->np = pdev->dev.of_node;
        power->regmap = axp20x->regmap;
 
-       /* Enable vbus valid checking */
-       ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
-                   AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID);
-       if (ret)
-               return ret;
+       if (of_device_is_compatible(power->np,
+                       "x-powers,axp202-usb-power-supply")) {
+               /* Enable vbus valid checking */
+               ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
+                                        AXP20X_VBUS_MON_VBUS_VALID,
+                                        AXP20X_VBUS_MON_VBUS_VALID);
+               if (ret)
+                       return ret;
 
-       /* Enable vbus voltage and current measurement */
-       ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
+               /* Enable vbus voltage and current measurement */
+               ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
                        AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
                        AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
-       if (ret)
-               return ret;
+               if (ret)
+                       return ret;
+
+               usb_power_desc = &axp20x_usb_power_desc;
+               irq_names = axp20x_irq_names;
+       } else if (of_device_is_compatible(power->np,
+                       "x-powers,axp221-usb-power-supply")) {
+               usb_power_desc = &axp22x_usb_power_desc;
+               irq_names = axp22x_irq_names;
+       } else {
+               dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+                       axp20x->variant);
+               return -EINVAL;
+       }
 
        psy_cfg.of_node = pdev->dev.of_node;
        psy_cfg.drv_data = power;
 
-       power->supply = devm_power_supply_register(&pdev->dev,
-                                       &axp20x_usb_power_desc, &psy_cfg);
+       power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc,
+                                                  &psy_cfg);
        if (IS_ERR(power->supply))
                return PTR_ERR(power->supply);
 
        /* Request irqs after registering, as irqs may trigger immediately */
-       for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
+       for (i = 0; irq_names[i]; i++) {
                irq = platform_get_irq_byname(pdev, irq_names[i]);
                if (irq < 0) {
                        dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
@@ -229,6 +274,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
 
 static const struct of_device_id axp20x_usb_power_match[] = {
        { .compatible = "x-powers,axp202-usb-power-supply" },
+       { .compatible = "x-powers,axp221-usb-power-supply" },
        { }
 };
 MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
index e90b3f3..323d05a 100644 (file)
@@ -82,6 +82,7 @@
  *
  * These are indexes into a device's register mapping array.
  */
+
 enum bq27xxx_reg_index {
        BQ27XXX_REG_CTRL = 0,   /* Control */
        BQ27XXX_REG_TEMP,       /* Temperature */
@@ -100,157 +101,144 @@ enum bq27xxx_reg_index {
        BQ27XXX_REG_SOC,        /* State-of-Charge */
        BQ27XXX_REG_DCAP,       /* Design Capacity */
        BQ27XXX_REG_AP,         /* Average Power */
+       BQ27XXX_REG_MAX,        /* sentinel */
 };
 
 /* Register mappings */
-static u8 bq27000_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       INVALID_REG_ADDR,       /* INT TEMP - NA*/
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       0x18,   /* TTF          */
-       0x1c,   /* TTES         */
-       0x26,   /* TTECP        */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       0x22,   /* AE           */
-       0x0b,   /* SOC(RSOC)    */
-       0x76,   /* DCAP(ILMD)   */
-       0x24,   /* AP           */
-};
-
-static u8 bq27010_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       INVALID_REG_ADDR,       /* INT TEMP - NA*/
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       0x18,   /* TTF          */
-       0x1c,   /* TTES         */
-       0x26,   /* TTECP        */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x0b,   /* SOC(RSOC)    */
-       0x76,   /* DCAP(ILMD)   */
-       INVALID_REG_ADDR,       /* AP - NA      */
-};
-
-static u8 bq27500_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       0x28,   /* INT TEMP     */
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       INVALID_REG_ADDR,       /* TTF - NA     */
-       0x1a,   /* TTES         */
-       INVALID_REG_ADDR,       /* TTECP - NA   */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x2c,   /* SOC(RSOC)    */
-       0x3c,   /* DCAP(ILMD)   */
-       INVALID_REG_ADDR,       /* AP - NA      */
-};
-
-static u8 bq27530_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       0x32,   /* INT TEMP     */
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       INVALID_REG_ADDR,       /* TTF - NA     */
-       INVALID_REG_ADDR,       /* TTES - NA    */
-       INVALID_REG_ADDR,       /* TTECP - NA   */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x2c,   /* SOC(RSOC)    */
-       INVALID_REG_ADDR,       /* DCAP - NA    */
-       0x24,   /* AP           */
-};
-
-static u8 bq27541_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       0x28,   /* INT TEMP     */
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       INVALID_REG_ADDR,       /* TTF - NA     */
-       INVALID_REG_ADDR,       /* TTES - NA    */
-       INVALID_REG_ADDR,       /* TTECP - NA   */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x2c,   /* SOC(RSOC)    */
-       0x3c,   /* DCAP         */
-       0x24,   /* AP           */
-};
-
-static u8 bq27545_regs[] = {
-       0x00,   /* CONTROL      */
-       0x06,   /* TEMP         */
-       0x28,   /* INT TEMP     */
-       0x08,   /* VOLT         */
-       0x14,   /* AVG CURR     */
-       0x0a,   /* FLAGS        */
-       0x16,   /* TTE          */
-       INVALID_REG_ADDR,       /* TTF - NA     */
-       INVALID_REG_ADDR,       /* TTES - NA    */
-       INVALID_REG_ADDR,       /* TTECP - NA   */
-       0x0c,   /* NAC          */
-       0x12,   /* LMD(FCC)     */
-       0x2a,   /* CYCT         */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x2c,   /* SOC(RSOC)    */
-       INVALID_REG_ADDR,       /* DCAP - NA */
-       0x24,   /* AP           */
-};
-
-static u8 bq27421_regs[] = {
-       0x00,   /* CONTROL      */
-       0x02,   /* TEMP         */
-       0x1e,   /* INT TEMP     */
-       0x04,   /* VOLT         */
-       0x10,   /* AVG CURR     */
-       0x06,   /* FLAGS        */
-       INVALID_REG_ADDR,       /* TTE - NA     */
-       INVALID_REG_ADDR,       /* TTF - NA     */
-       INVALID_REG_ADDR,       /* TTES - NA    */
-       INVALID_REG_ADDR,       /* TTECP - NA   */
-       0x08,   /* NAC          */
-       0x0e,   /* FCC          */
-       INVALID_REG_ADDR,       /* CYCT - NA    */
-       INVALID_REG_ADDR,       /* AE - NA      */
-       0x1c,   /* SOC          */
-       0x3c,   /* DCAP         */
-       0x18,   /* AP           */
-};
-
-static u8 *bq27xxx_regs[] = {
-       [BQ27000] = bq27000_regs,
-       [BQ27010] = bq27010_regs,
-       [BQ27500] = bq27500_regs,
-       [BQ27530] = bq27530_regs,
-       [BQ27541] = bq27541_regs,
-       [BQ27545] = bq27545_regs,
-       [BQ27421] = bq27421_regs,
+static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
+       [BQ27000] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = 0x18,
+               [BQ27XXX_REG_TTES] = 0x1c,
+               [BQ27XXX_REG_TTECP] = 0x26,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = 0x22,
+               [BQ27XXX_REG_SOC] = 0x0b,
+               [BQ27XXX_REG_DCAP] = 0x76,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27010] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = 0x18,
+               [BQ27XXX_REG_TTES] = 0x1c,
+               [BQ27XXX_REG_TTECP] = 0x26,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x0b,
+               [BQ27XXX_REG_DCAP] = 0x76,
+               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+       },
+       [BQ27500] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = 0x1a,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+       },
+       [BQ27530] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x32,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27541] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27545] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27421] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x02,
+               [BQ27XXX_REG_INT_TEMP] = 0x1e,
+               [BQ27XXX_REG_VOLT] = 0x04,
+               [BQ27XXX_REG_AI] = 0x10,
+               [BQ27XXX_REG_FLAGS] = 0x06,
+               [BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x08,
+               [BQ27XXX_REG_FCC] = 0x0e,
+               [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x1c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = 0x18,
+       },
 };
 
 static enum power_supply_property bq27000_battery_props[] = {
index b8f8d3a..85d4ea2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * SCI Reset driver for Keystone based devices
+ * BQ27xxx battery monitor I2C driver
  *
  * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
  *     Andrew F. Davis <afd@ti.com>
index 17876ca..fdc73d6 100644 (file)
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <linux/power_supply.h>
 #include <linux/platform_device.h>
 #include <linux/power/max8903_charger.h>
 
 struct max8903_data {
-       struct max8903_pdata pdata;
+       struct max8903_pdata *pdata;
        struct device *dev;
        struct power_supply *psy;
        struct power_supply_desc psy_desc;
@@ -53,8 +56,8 @@ static int max8903_get_property(struct power_supply *psy,
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
                val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               if (data->pdata.chg) {
-                       if (gpio_get_value(data->pdata.chg) == 0)
+               if (gpio_is_valid(data->pdata->chg)) {
+                       if (gpio_get_value(data->pdata->chg) == 0)
                                val->intval = POWER_SUPPLY_STATUS_CHARGING;
                        else if (data->usb_in || data->ta_in)
                                val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -75,13 +78,14 @@ static int max8903_get_property(struct power_supply *psy,
        default:
                return -EINVAL;
        }
+
        return 0;
 }
 
 static irqreturn_t max8903_dcin(int irq, void *_data)
 {
        struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = &data->pdata;
+       struct max8903_pdata *pdata = data->pdata;
        bool ta_in;
        enum power_supply_type old_type;
 
@@ -93,11 +97,11 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
        data->ta_in = ta_in;
 
        /* Set Current-Limit-Mode 1:DC 0:USB */
-       if (pdata->dcm)
+       if (gpio_is_valid(pdata->dcm))
                gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
 
        /* Charger Enable / Disable (cen is negated) */
-       if (pdata->cen)
+       if (gpio_is_valid(pdata->cen))
                gpio_set_value(pdata->cen, ta_in ? 0 :
                                (data->usb_in ? 0 : 1));
 
@@ -122,7 +126,7 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
 static irqreturn_t max8903_usbin(int irq, void *_data)
 {
        struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = &data->pdata;
+       struct max8903_pdata *pdata = data->pdata;
        bool usb_in;
        enum power_supply_type old_type;
 
@@ -136,7 +140,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
        /* Do not touch Current-Limit-Mode */
 
        /* Charger Enable / Disable (cen is negated) */
-       if (pdata->cen)
+       if (gpio_is_valid(pdata->cen))
                gpio_set_value(pdata->cen, usb_in ? 0 :
                                (data->ta_in ? 0 : 1));
 
@@ -161,7 +165,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
 static irqreturn_t max8903_fault(int irq, void *_data)
 {
        struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = &data->pdata;
+       struct max8903_pdata *pdata = data->pdata;
        bool fault;
 
        fault = gpio_get_value(pdata->flt) ? false : true;
@@ -179,57 +183,109 @@ static irqreturn_t max8903_fault(int irq, void *_data)
        return IRQ_HANDLED;
 }
 
-static int max8903_probe(struct platform_device *pdev)
+static struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
 {
-       struct max8903_data *data;
+       struct device_node *np = dev->of_node;
+       struct max8903_pdata *pdata = NULL;
+
+       if (!np)
+               return NULL;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->dc_valid = false;
+       pdata->usb_valid = false;
+
+       pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
+       if (!gpio_is_valid(pdata->cen))
+               pdata->cen = -EINVAL;
+
+       pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
+       if (!gpio_is_valid(pdata->chg))
+               pdata->chg = -EINVAL;
+
+       pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
+       if (!gpio_is_valid(pdata->flt))
+               pdata->flt = -EINVAL;
+
+       pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
+       if (!gpio_is_valid(pdata->usus))
+               pdata->usus = -EINVAL;
+
+       pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
+       if (!gpio_is_valid(pdata->dcm))
+               pdata->dcm = -EINVAL;
+
+       pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
+       if (!gpio_is_valid(pdata->dok))
+               pdata->dok = -EINVAL;
+       else
+               pdata->dc_valid = true;
+
+       pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
+       if (!gpio_is_valid(pdata->uok))
+               pdata->uok = -EINVAL;
+       else
+               pdata->usb_valid = true;
+
+       return pdata;
+}
+
+static int max8903_setup_gpios(struct platform_device *pdev)
+{
+       struct max8903_data *data = platform_get_drvdata(pdev);
        struct device *dev = &pdev->dev;
        struct max8903_pdata *pdata = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
        int ret = 0;
        int gpio;
        int ta_in = 0;
        int usb_in = 0;
 
-       data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
-       if (data == NULL) {
-               dev_err(dev, "Cannot allocate memory.\n");
-               return -ENOMEM;
-       }
-       memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata));
-       data->dev = dev;
-       platform_set_drvdata(pdev, data);
-
-       if (pdata->dc_valid == false && pdata->usb_valid == false) {
-               dev_err(dev, "No valid power sources.\n");
-               return -EINVAL;
-       }
-
        if (pdata->dc_valid) {
-               if (pdata->dok && gpio_is_valid(pdata->dok) &&
-                               pdata->dcm && gpio_is_valid(pdata->dcm)) {
+               if (gpio_is_valid(pdata->dok)) {
+                       ret = devm_gpio_request(dev, pdata->dok,
+                                               data->psy_desc.name);
+                       if (ret) {
+                               dev_err(dev,
+                                       "Failed GPIO request for dok: %d err %d\n",
+                                       pdata->dok, ret);
+                               return ret;
+                       }
+
                        gpio = pdata->dok; /* PULL_UPed Interrupt */
                        ta_in = gpio_get_value(gpio) ? 0 : 1;
-
-                       gpio = pdata->dcm; /* Output */
-                       gpio_set_value(gpio, ta_in);
                } else {
-                       dev_err(dev, "When DC is wired, DOK and DCM should"
-                                       " be wired as well.\n");
+                       dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
                        return -EINVAL;
                }
-       } else {
-               if (pdata->dcm) {
-                       if (gpio_is_valid(pdata->dcm))
-                               gpio_set_value(pdata->dcm, 0);
-                       else {
-                               dev_err(dev, "Invalid pin: dcm.\n");
-                               return -EINVAL;
-                       }
+       }
+
+       if (gpio_is_valid(pdata->dcm)) {
+               ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for dcm: %d err %d\n",
+                               pdata->dcm, ret);
+                       return ret;
                }
+
+               gpio = pdata->dcm; /* Output */
+               gpio_set_value(gpio, ta_in);
        }
 
        if (pdata->usb_valid) {
-               if (pdata->uok && gpio_is_valid(pdata->uok)) {
+               if (gpio_is_valid(pdata->uok)) {
+                       ret = devm_gpio_request(dev, pdata->uok,
+                                               data->psy_desc.name);
+                       if (ret) {
+                               dev_err(dev,
+                                       "Failed GPIO request for uok: %d err %d\n",
+                                       pdata->uok, ret);
+                               return ret;
+                       }
+
                        gpio = pdata->uok;
                        usb_in = gpio_get_value(gpio) ? 0 : 1;
                } else {
@@ -239,33 +295,45 @@ static int max8903_probe(struct platform_device *pdev)
                }
        }
 
-       if (pdata->cen) {
-               if (gpio_is_valid(pdata->cen)) {
-                       gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
-               } else {
-                       dev_err(dev, "Invalid pin: cen.\n");
-                       return -EINVAL;
+       if (gpio_is_valid(pdata->cen)) {
+               ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for cen: %d err %d\n",
+                               pdata->cen, ret);
+                       return ret;
                }
+
+               gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
        }
 
-       if (pdata->chg) {
-               if (!gpio_is_valid(pdata->chg)) {
-                       dev_err(dev, "Invalid pin: chg.\n");
-                       return -EINVAL;
+       if (gpio_is_valid(pdata->chg)) {
+               ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for chg: %d err %d\n",
+                               pdata->chg, ret);
+                       return ret;
                }
        }
 
-       if (pdata->flt) {
-               if (!gpio_is_valid(pdata->flt)) {
-                       dev_err(dev, "Invalid pin: flt.\n");
-                       return -EINVAL;
+       if (gpio_is_valid(pdata->flt)) {
+               ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for flt: %d err %d\n",
+                               pdata->flt, ret);
+                       return ret;
                }
        }
 
-       if (pdata->usus) {
-               if (!gpio_is_valid(pdata->usus)) {
-                       dev_err(dev, "Invalid pin: usus.\n");
-                       return -EINVAL;
+       if (gpio_is_valid(pdata->usus)) {
+               ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for usus: %d err %d\n",
+                               pdata->usus, ret);
+                       return ret;
                }
        }
 
@@ -273,14 +341,52 @@ static int max8903_probe(struct platform_device *pdev)
        data->ta_in = ta_in;
        data->usb_in = usb_in;
 
+       return 0;
+}
+
+static int max8903_probe(struct platform_device *pdev)
+{
+       struct max8903_data *data;
+       struct device *dev = &pdev->dev;
+       struct max8903_pdata *pdata = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+
+       data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
+               pdata = max8903_parse_dt_data(dev);
+
+       if (!pdata) {
+               dev_err(dev, "No platform data.\n");
+               return -EINVAL;
+       }
+
+       pdev->dev.platform_data = pdata;
+       data->pdata = pdata;
+       data->dev = dev;
+       platform_set_drvdata(pdev, data);
+
+       if (pdata->dc_valid == false && pdata->usb_valid == false) {
+               dev_err(dev, "No valid power sources.\n");
+               return -EINVAL;
+       }
+
+       ret = max8903_setup_gpios(pdev);
+       if (ret)
+               return ret;
+
        data->psy_desc.name = "max8903_charger";
-       data->psy_desc.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS :
-                       ((usb_in) ? POWER_SUPPLY_TYPE_USB :
+       data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
+                       ((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
                         POWER_SUPPLY_TYPE_BATTERY);
        data->psy_desc.get_property = max8903_get_property;
        data->psy_desc.properties = max8903_charger_props;
        data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
 
+       psy_cfg.of_node = dev->of_node;
        psy_cfg.drv_data = data;
 
        data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
@@ -315,7 +421,7 @@ static int max8903_probe(struct platform_device *pdev)
                }
        }
 
-       if (pdata->flt) {
+       if (gpio_is_valid(pdata->flt)) {
                ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
                                        NULL, max8903_fault,
                                        IRQF_TRIGGER_FALLING |
@@ -331,10 +437,17 @@ static int max8903_probe(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id max8903_match_ids[] = {
+       { .compatible = "maxim,max8903", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max8903_match_ids);
+
 static struct platform_driver max8903_driver = {
        .probe  = max8903_probe,
        .driver = {
                .name   = "max8903-charger",
+               .of_match_table = max8903_match_ids
        },
 };
 
index b13cd07..a74d8ca 100644 (file)
@@ -491,8 +491,11 @@ int power_supply_get_property(struct power_supply *psy,
                            enum power_supply_property psp,
                            union power_supply_propval *val)
 {
-       if (atomic_read(&psy->use_cnt) <= 0)
+       if (atomic_read(&psy->use_cnt) <= 0) {
+               if (!psy->initialized)
+                       return -EAGAIN;
                return -ENODEV;
+       }
 
        return psy->desc->get_property(psy, psp, val);
 }
@@ -785,6 +788,7 @@ __power_supply_register(struct device *parent,
         *    after calling power_supply_register()).
         */
        atomic_inc(&psy->use_cnt);
+       psy->initialized = true;
 
        queue_delayed_work(system_power_efficient_wq,
                           &psy->deferred_register_work,
index 80fed98..bcde8d1 100644 (file)
@@ -83,7 +83,7 @@ static ssize_t power_supply_show_property(struct device *dev,
                        if (ret == -ENODATA)
                                dev_dbg(dev, "driver has no data for `%s' property\n",
                                        attr->attr.name);
-                       else if (ret != -ENODEV)
+                       else if (ret != -ENODEV && ret != -EAGAIN)
                                dev_err(dev, "driver failed to report `%s' property: %zd\n",
                                        attr->attr.name, ret);
                        return ret;
index 5eb1e9e..b5896ba 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/power_supply.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/extcon.h>
 
 #define SMBB_CHG_VMAX          0x040
 #define SMBB_CHG_VSAFE         0x041
@@ -111,6 +112,7 @@ struct smbb_charger {
        unsigned int revision;
        unsigned int addr;
        struct device *dev;
+       struct extcon_dev *edev;
 
        bool dc_disabled;
        bool jeita_ext_temp;
@@ -125,6 +127,11 @@ struct smbb_charger {
        struct regmap *regmap;
 };
 
+static const unsigned int smbb_usb_extcon_cable[] = {
+       EXTCON_USB,
+       EXTCON_NONE,
+};
+
 static int smbb_vbat_weak_fn(unsigned int index)
 {
        return 2100000 + index * 100000;
@@ -371,6 +378,8 @@ static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
        struct smbb_charger *chg = _data;
 
        smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
+       extcon_set_cable_state_(chg->edev, EXTCON_USB,
+                               chg->status & STATUS_USBIN_VALID);
        power_supply_changed(chg->usb_psy);
 
        return IRQ_HANDLED;
@@ -849,6 +858,18 @@ static int smbb_charger_probe(struct platform_device *pdev)
                return PTR_ERR(chg->usb_psy);
        }
 
+       chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable);
+       if (IS_ERR(chg->edev)) {
+               dev_err(&pdev->dev, "failed to allocate extcon device\n");
+               return -ENOMEM;
+       }
+
+       rc = devm_extcon_dev_register(&pdev->dev, chg->edev);
+       if (rc < 0) {
+               dev_err(&pdev->dev, "failed to register extcon device\n");
+               return rc;
+       }
+
        if (!chg->dc_disabled) {
                dc_cfg.drv_data = chg;
                dc_cfg.supplied_to = smbb_bif;
index 9bb2622..7053abc 100644 (file)
@@ -148,7 +148,8 @@ config POWER_RESET_XGENE
 
 config POWER_RESET_KEYSTONE
        bool "Keystone reset driver"
-       depends on ARCH_KEYSTONE
+       depends on ARCH_KEYSTONE || COMPILE_TEST
+       depends on HAS_IOMEM
        select MFD_SYSCON
        help
          Reboot support for the KEYSTONE SoCs.
@@ -183,5 +184,19 @@ config POWER_RESET_ZX
        help
          Reboot support for ZTE SoCs.
 
+config REBOOT_MODE
+       tristate
+
+config SYSCON_REBOOT_MODE
+       tristate "Generic SYSCON regmap reboot mode driver"
+       depends on OF
+       select REBOOT_MODE
+       select MFD_SYSCON
+       help
+         Say y here will enable reboot mode driver. This will
+         get reboot mode arguments and store it in SYSCON mapped
+         register, then the bootloader can read it to take different
+         action according to the mode.
+
 endif
 
index ab7aa86..d6b2560 100644 (file)
@@ -21,3 +21,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
 obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
 obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
 obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
+obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
+obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
new file mode 100644 (file)
index 0000000..2dfbbce
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include "reboot-mode.h"
+
+#define PREFIX "mode-"
+
+struct mode_info {
+       const char *mode;
+       u32 magic;
+       struct list_head list;
+};
+
+static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
+                                         const char *cmd)
+{
+       const char *normal = "normal";
+       int magic = 0;
+       struct mode_info *info;
+
+       if (!cmd)
+               cmd = normal;
+
+       list_for_each_entry(info, &reboot->head, list) {
+               if (!strcmp(info->mode, cmd)) {
+                       magic = info->magic;
+                       break;
+               }
+       }
+
+       return magic;
+}
+
+static int reboot_mode_notify(struct notifier_block *this,
+                             unsigned long mode, void *cmd)
+{
+       struct reboot_mode_driver *reboot;
+       unsigned int magic;
+
+       reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
+       magic = get_reboot_mode_magic(reboot, cmd);
+       if (magic)
+               reboot->write(reboot, magic);
+
+       return NOTIFY_DONE;
+}
+
+/**
+ * reboot_mode_register - register a reboot mode driver
+ * @reboot: reboot mode driver
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int reboot_mode_register(struct reboot_mode_driver *reboot)
+{
+       struct mode_info *info;
+       struct property *prop;
+       struct device_node *np = reboot->dev->of_node;
+       size_t len = strlen(PREFIX);
+       int ret;
+
+       INIT_LIST_HEAD(&reboot->head);
+
+       for_each_property_of_node(np, prop) {
+               if (strncmp(prop->name, PREFIX, len))
+                       continue;
+
+               info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
+               if (!info) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               if (of_property_read_u32(np, prop->name, &info->magic)) {
+                       dev_err(reboot->dev, "reboot mode %s without magic number\n",
+                               info->mode);
+                       devm_kfree(reboot->dev, info);
+                       continue;
+               }
+
+               info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
+               if (!info->mode) {
+                       ret =  -ENOMEM;
+                       goto error;
+               } else if (info->mode[0] == '\0') {
+                       kfree_const(info->mode);
+                       ret = -EINVAL;
+                       dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
+                               prop->name);
+                       goto error;
+               }
+
+               list_add_tail(&info->list, &reboot->head);
+       }
+
+       reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+       register_reboot_notifier(&reboot->reboot_notifier);
+
+       return 0;
+
+error:
+       list_for_each_entry(info, &reboot->head, list)
+               kfree_const(info->mode);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_register);
+
+/**
+ * reboot_mode_unregister - unregister a reboot mode driver
+ * @reboot: reboot mode driver
+ */
+int reboot_mode_unregister(struct reboot_mode_driver *reboot)
+{
+       struct mode_info *info;
+
+       unregister_reboot_notifier(&reboot->reboot_notifier);
+
+       list_for_each_entry(info, &reboot->head, list)
+               kfree_const(info->mode);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_unregister);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("System reboot mode core library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
new file mode 100644 (file)
index 0000000..2491bb7
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __REBOOT_MODE_H__
+#define __REBOOT_MODE_H__
+
+struct reboot_mode_driver {
+       struct device *dev;
+       struct list_head head;
+       int (*write)(struct reboot_mode_driver *reboot, unsigned int magic);
+       struct notifier_block reboot_notifier;
+};
+
+int reboot_mode_register(struct reboot_mode_driver *reboot);
+int reboot_mode_unregister(struct reboot_mode_driver *reboot);
+
+#endif
index 5560b0d..b683383 100644 (file)
@@ -30,7 +30,7 @@ static struct regmap *map;
 static u32 offset;
 static u32 mask;
 
-void syscon_poweroff(void)
+static void syscon_poweroff(void)
 {
        /* Issue the poweroff */
        regmap_write(map, offset, mask);
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
new file mode 100644 (file)
index 0000000..9e1cba5
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include "reboot-mode.h"
+
+struct syscon_reboot_mode {
+       struct regmap *map;
+       struct reboot_mode_driver reboot;
+       u32 offset;
+       u32 mask;
+};
+
+static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
+                                   unsigned int magic)
+{
+       struct syscon_reboot_mode *syscon_rbm;
+       int ret;
+
+       syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot);
+
+       ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset,
+                                syscon_rbm->mask, magic);
+       if (ret < 0)
+               dev_err(reboot->dev, "update reboot mode bits failed\n");
+
+       return ret;
+}
+
+static int syscon_reboot_mode_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct syscon_reboot_mode *syscon_rbm;
+
+       syscon_rbm = devm_kzalloc(&pdev->dev, sizeof(*syscon_rbm), GFP_KERNEL);
+       if (!syscon_rbm)
+               return -ENOMEM;
+
+       syscon_rbm->reboot.dev = &pdev->dev;
+       syscon_rbm->reboot.write = syscon_reboot_mode_write;
+       syscon_rbm->mask = 0xffffffff;
+
+       dev_set_drvdata(&pdev->dev, syscon_rbm);
+
+       syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+       if (IS_ERR(syscon_rbm->map))
+               return PTR_ERR(syscon_rbm->map);
+
+       if (of_property_read_u32(pdev->dev.of_node, "offset",
+           &syscon_rbm->offset))
+               return -EINVAL;
+
+       of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask);
+
+       ret = reboot_mode_register(&syscon_rbm->reboot);
+       if (ret)
+               dev_err(&pdev->dev, "can't register reboot mode\n");
+
+       return ret;
+}
+
+static int syscon_reboot_mode_remove(struct platform_device *pdev)
+{
+       struct syscon_reboot_mode *syscon_rbm = dev_get_drvdata(&pdev->dev);
+
+       return reboot_mode_unregister(&syscon_rbm->reboot);
+}
+
+static const struct of_device_id syscon_reboot_mode_of_match[] = {
+       { .compatible = "syscon-reboot-mode" },
+       {}
+};
+
+static struct platform_driver syscon_reboot_mode_driver = {
+       .probe = syscon_reboot_mode_probe,
+       .remove = syscon_reboot_mode_remove,
+       .driver = {
+               .name = "syscon-reboot-mode",
+               .of_match_table = syscon_reboot_mode_of_match,
+       },
+};
+module_platform_driver(syscon_reboot_mode_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("SYSCON reboot mode driver");
+MODULE_LICENSE("GPL v2");
index 24f51db..89d3f1c 100644 (file)
@@ -26,8 +26,8 @@
 struct max8903_pdata {
        /*
         * GPIOs
-        * cen, chg, flt, and usus are optional.
-        * dok, dcm, and uok are not optional depending on the status of
+        * cen, chg, flt, dcm and usus are optional.
+        * dok and uok are not optional depending on the status of
         * dc_valid and usb_valid.
         */
        int cen;        /* Charger Enable input */
@@ -41,7 +41,7 @@ struct max8903_pdata {
        /*
         * DC(Adapter/TA) is wired
         * When dc_valid is true,
-        *      dok and dcm should be valid.
+        *      dok should be valid.
         *
         * At least one of dc_valid or usb_valid should be true.
         */
index 7510617..3965503 100644 (file)
@@ -248,6 +248,7 @@ struct power_supply {
        struct delayed_work deferred_register_work;
        spinlock_t changed_lock;
        bool changed;
+       bool initialized;
        atomic_t use_cnt;
 #ifdef CONFIG_THERMAL
        struct thermal_zone_device *tzd;