diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 10:00:10 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 10:00:10 -0800 | 
| commit | c9b9f207b90468bf9583f7ed71c15d0142bbf9b1 (patch) | |
| tree | e6f1576e41cffa36da3a688bfb372aed199975c2 | |
| parent | 345fb0a9a6340ba0b3b714d62f877fde88c8a755 (diff) | |
| parent | 744cc304a18f1c9de4f3215fbe93fe878f934179 (diff) | |
Merge tag 'for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
 "New drivers:
   - sbs-charger driver
   - max14656_charger_detector
   - axp20x_ac_power
  New chip/feature support"
   - axp20x_usb_power: add AXP223 support
   - tps65217: add usb charger support
   - qcom_smbb: support otg regulator
   - at91-reset: add samx7 support
  Dropped drivers:
   - intel_mid_battery (platform was dropped)
  Fixes:
   - at91-poweroff: avoid wearing off LPDDR memory
   - replace deprecated extcon API
   - lots of cleanup and style fixes
   - misc minor functionality fixes"
* tag 'for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (84 commits)
  power: supply: add AC power supply driver for AXP20X and AXP22X PMICs
  dt-bindings: power: supply: add AXP20X/AXP22X AC power supply
  power: supply: axp20x_usb_power: use IIO channels when available
  power: supply: max14656: Export I2C and OF device ID as module aliases
  power: supply: bq2415x: check for NULL acpi_id to avoid null pointer dereference
  power: supply: bq24190_charger: Adjust formatting
  power: supply: bq24190_charger: Handle fault before status on interrupt
  power: supply: bq24190_charger: Don't read fault register outside irq_handle_thread()
  power: supply: bq24190_charger: Call power_supply_changed() for relevant component
  power: supply: bq24190_charger: Install irq_handler_thread() at end of probe()
  power: supply: bq24190_charger: Call set_mode_host() on pm_resume()
  power: supply: bq24190_charger: Fix irq trigger to IRQF_TRIGGER_FALLING
  power: supply: qcom_smbb: add regulator dependency
  power: reset: at91-reset: remove leftover platform_device_id
  power: reset: at91-reset: add samx7 support
  power: supply: max14656: fix platform_no_drv_owner.cocci warnings
  power: supply: pcf50633-charger: Compress return logic into one line.
  power: supply: ab8500_btemp: Compress return logic into one line.
  power: reset: at91-poweroff: timely shutdown LPDDR memories
  ARM: at91: define LPDDR types
  ...
35 files changed, 2274 insertions, 1818 deletions
diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt new file mode 100644 index 000000000000..826e8a879121 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt @@ -0,0 +1,22 @@ +AXP20X and AXP22X PMICs' AC power supply + +Required Properties: + - compatible: One of: +			"x-powers,axp202-ac-power-supply" +			"x-powers,axp221-ac-power-supply" + +This node is a subnode of the axp20x PMIC. + +The AXP20X can read the current current and voltage supplied by AC by +reading ADC channels from the AXP20X ADC. + +The AXP22X is only able to tell if an AC power supply is present and +usable. + +Example: + +&axp209 { +	ac_power_supply: ac-power-supply { +		compatible = "x-powers,axp202-ac-power-supply"; +	}; +}; diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt index f1d7beec45bf..ba8d35f66cbe 100644 --- a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt +++ b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt @@ -3,6 +3,11 @@ AXP20x USB power supply  Required Properties:  -compatible: One of: "x-powers,axp202-usb-power-supply"                       "x-powers,axp221-usb-power-supply" +                     "x-powers,axp223-usb-power-supply" + +The AXP223 PMIC shares most of its behaviour with the AXP221 but has slight +variations such as the former being able to set the VBUS power supply max +current to 100mA, unlike the latter.  This node is a subnode of the axp20x PMIC. diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt new file mode 100644 index 000000000000..b0c95ef63e68 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt @@ -0,0 +1,36 @@ +Binding for TI BQ27XXX fuel gauge family + +Required properties: +- compatible: Should contain one of the following: + * "ti,bq27200" - BQ27200 + * "ti,bq27210" - BQ27210 + * "ti,bq27500" - deprecated, use revision specific property below + * "ti,bq27510" - deprecated, use revision specific property below + * "ti,bq27520" - deprecated, use revision specific property below + * "ti,bq27500-1" - BQ27500/1 + * "ti,bq27510g1" - BQ27510-g1 + * "ti,bq27510g2" - BQ27510-g2 + * "ti,bq27510g3" - BQ27510-g3 + * "ti,bq27520g1" - BQ27520-g1 + * "ti,bq27520g2" - BQ27520-g2 + * "ti,bq27520g3" - BQ27520-g3 + * "ti,bq27520g4" - BQ27520-g4 + * "ti,bq27530" - BQ27530 + * "ti,bq27531" - BQ27531 + * "ti,bq27541" - BQ27541 + * "ti,bq27542" - BQ27542 + * "ti,bq27546" - BQ27546 + * "ti,bq27742" - BQ27742 + * "ti,bq27545" - BQ27545 + * "ti,bq27421" - BQ27421 + * "ti,bq27425" - BQ27425 + * "ti,bq27441" - BQ27441 + * "ti,bq27621" - BQ27621 +- reg: integer, i2c address of the device. + +Example: + +bq27510g3 { +    compatible = "ti,bq27510g3"; +    reg = <0x55>; +}; diff --git a/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt b/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt index 65b88fac854b..06f8a5ddb68e 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt @@ -105,6 +105,22 @@ PROPERTIES                 regulation must be done externally to fully comply with                 the JEITA safety guidelines if this flag is set. +- usb_otg_in-supply: +  Usage: optional +  Value type: <phandle> +  Description: Reference to the regulator supplying power to the USB_OTG_IN +               pin. + +child nodes: +- otg-vbus: +  Usage: optional +  Description: This node defines a regulator used to control the direction +               of VBUS voltage - specifically: whether to supply voltage +               to VBUS for host mode operation of the OTG port, or allow +               input voltage from external VBUS for charging.  In the +               hardware, the supply for this regulator comes from +               usb_otg_in-supply. +  EXAMPLE  charger@1000 {         compatible = "qcom,pm8941-charger"; @@ -128,4 +144,7 @@ charger@1000 {         qcom,fast-charge-current-limit = <1000000>;         qcom,dc-charge-current-limit = <1000000>; +       usb_otg_in-supply = <&pm8941_5vs1>; + +       otg-vbus {};  }; diff --git a/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt b/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt new file mode 100644 index 000000000000..a3719623a94f --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt @@ -0,0 +1,23 @@ +SBS sbs-charger +~~~~~~~~~~ + +Required properties: + - compatible: "<vendor>,<part-number>", "sbs,sbs-charger" as fallback. The part +     number compatible string might be used in order to take care of vendor +     specific registers. + +Optional properties: +- interrupt-parent: Should be the phandle for the interrupt controller. Use in +    conjunction with "interrupts". +- interrupts: Interrupt mapping for GPIO IRQ. Use in conjunction with +    "interrupt-parent". If an interrupt is not provided the driver will switch +    automatically to polling. + +Example: + +	ltc4100@9 { +		compatible = "lltc,ltc4100", "sbs,sbs-charger"; +		reg = <0x9>; +		interrupt-parent = <&gpio6>; +		interrupts = <7 IRQ_TYPE_LEVEL_LOW>; +	}; diff --git a/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt index 3bf55757ceec..de45e1a2a4d9 100644 --- a/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt +++ b/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt @@ -8,8 +8,10 @@ Optional properties :   - interrupts : Specify the interrupt to be used to trigger when the AC     adapter is either plugged in or removed.   - ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter -   presence. This is a Host GPIO that is configured as an input and -   connected to the bq24735. +   status. This is a Host GPIO that is configured as an input and connected +   to the ACOK pin on the bq24735. Note: for backwards compatibility reasons, +   the GPIO must be active on AC adapter absence despite ACOK being active +   (high) on AC adapter presence.   - ti,charge-current : Used to control and set the charging current. This value     must be between 128mA and 8.128A with a 64mA step resolution. The POR value     is 0x0000h. This number is in mA (e.g. 8192), see spec for more information @@ -25,6 +27,8 @@ Optional properties :   - ti,external-control : Indicates that the charger is configured externally     and that the host should not attempt to enable/disable charging or set the     charge voltage/current. + - poll-interval : In case 'interrupts' is not specified, poll AC adapter +   presence with this interval (milliseconds).  Example: diff --git a/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt b/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt new file mode 100644 index 000000000000..e03e85ae6572 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt @@ -0,0 +1,25 @@ +Maxim MAX14656 / AL32 USB Charger Detector + +Required properties : +- compatible : "maxim,max14656"; +- reg: i2c slave address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: interrupt line + +Example: + +&i2c2 { +	clock-frequency = <50000>; +	pinctrl-names = "default"; +	pinctrl-0 = <&pinctrl_i2c2>; +	status = "okay"; + +	max14656@35 { +		compatible = "maxim,max14656"; +		reg = <0x35>; +		pinctrl-names = "default"; +		pinctrl-0 = <&pinctrl_charger_detect>; +		interrupt-parent = <&gpio6>; +		interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; +	}; +}; diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index abeb77217a21..b8cacccf18c8 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -32,7 +32,7 @@ config POWER_RESET_AT91_RESET  config POWER_RESET_AT91_SAMA5D2_SHDWC  	tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver" -	depends on ARCH_AT91 || COMPILE_TEST +	depends on ARCH_AT91  	default SOC_SAMA5  	help  	  This driver supports the alternate shutdown controller for some Atmel diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index a85dd4d233af..c6c3beea72f9 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -14,9 +14,12 @@  #include <linux/io.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/of_address.h>  #include <linux/platform_device.h>  #include <linux/printk.h> +#include <soc/at91/at91sam9_ddrsdr.h> +  #define AT91_SHDW_CR	0x00		/* Shut Down Control Register */  #define AT91_SHDW_SHDW		BIT(0)			/* Shut Down command */  #define AT91_SHDW_KEY		(0xa5 << 24)		/* KEY Password */ @@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {  static void __iomem *at91_shdwc_base;  static struct clk *sclk; +static void __iomem *mpddrc_base;  static void __init at91_wakeup_status(void)  { @@ -73,6 +77,29 @@ static void at91_poweroff(void)  	writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);  } +static void at91_lpddr_poweroff(void) +{ +	asm volatile( +		/* Align to cache lines */ +		".balign 32\n\t" + +		/* Ensure AT91_SHDW_CR is in the TLB by reading it */ +		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + +		/* Power down SDRAM0 */ +		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" +		/* Shutdown CPU */ +		"	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + +		"	b	.\n\t" +		: +		: "r" (mpddrc_base), +		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), +		  "r" (at91_shdwc_base), +		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) +		: "r0"); +} +  static int at91_poweroff_get_wakeup_mode(struct device_node *np)  {  	const char *pm; @@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)  static int __init at91_poweroff_probe(struct platform_device *pdev)  {  	struct resource *res; +	struct device_node *np; +	u32 ddr_type;  	int ret;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -150,12 +179,30 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)  	pm_power_off = at91_poweroff; +	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); +	if (!np) +		return 0; + +	mpddrc_base = of_iomap(np, 0); +	of_node_put(np); + +	if (!mpddrc_base) +		return 0; + +	ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; +	if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || +	    (ddr_type == AT91_DDRSDRC_MD_LPDDR3)) +		pm_power_off = at91_lpddr_poweroff; +	else +		iounmap(mpddrc_base); +  	return 0;  }  static int __exit at91_poweroff_remove(struct platform_device *pdev)  { -	if (pm_power_off == at91_poweroff) +	if (pm_power_off == at91_poweroff || +	    pm_power_off == at91_lpddr_poweroff)  		pm_power_off = NULL;  	clk_disable_unprepare(sclk); @@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)  	return 0;  } +static const struct of_device_id at91_ramc_of_match[] = { +	{ .compatible = "atmel,sama5d3-ddramc", }, +	{ /* sentinel */ } +}; +  static const struct of_device_id at91_poweroff_of_match[] = {  	{ .compatible = "atmel,at91sam9260-shdwc", },  	{ .compatible = "atmel,at91sam9rl-shdwc", }, diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 568580cf0655..b99769f8ab15 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -134,6 +134,15 @@ static int sama5d3_restart(struct notifier_block *this, unsigned long mode,  	return NOTIFY_DONE;  } +static int samx7_restart(struct notifier_block *this, unsigned long mode, +			 void *cmd) +{ +	writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PROCRST), +	       at91_rstc_base); + +	return NOTIFY_DONE; +} +  static void __init at91_reset_status(struct platform_device *pdev)  {  	u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); @@ -173,6 +182,7 @@ static const struct of_device_id at91_reset_of_match[] = {  	{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },  	{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },  	{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart }, +	{ .compatible = "atmel,samx7-rstc", .data = samx7_restart },  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, at91_reset_of_match); @@ -238,20 +248,12 @@ static int __exit at91_reset_remove(struct platform_device *pdev)  	return 0;  } -static const struct platform_device_id at91_reset_plat_match[] = { -	{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart }, -	{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart }, -	{ /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, at91_reset_plat_match); -  static struct platform_driver at91_reset_driver = {  	.remove = __exit_p(at91_reset_remove),  	.driver = {  		.name = "at91-reset",  		.of_match_table = at91_reset_of_match,  	}, -	.id_table = at91_reset_plat_match,  };  module_platform_driver_probe(at91_reset_driver, at91_reset_probe); diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 8a5ac9706c9c..90b0b5a70ce5 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -22,9 +22,12 @@  #include <linux/io.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/of_address.h>  #include <linux/platform_device.h>  #include <linux/printk.h> +#include <soc/at91/at91sam9_ddrsdr.h> +  #define SLOW_CLOCK_FREQ	32768  #define AT91_SHDW_CR	0x00		/* Shut Down Control Register */ @@ -75,6 +78,7 @@ struct shdwc {   */  static struct shdwc *at91_shdwc;  static struct clk *sclk; +static void __iomem *mpddrc_base;  static const unsigned long long sdwc_dbc_period[] = {  	0, 3, 32, 512, 4096, 32768, @@ -108,6 +112,29 @@ static void at91_poweroff(void)  	       at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);  } +static void at91_lpddr_poweroff(void) +{ +	asm volatile( +		/* Align to cache lines */ +		".balign 32\n\t" + +		/* Ensure AT91_SHDW_CR is in the TLB by reading it */ +		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + +		/* Power down SDRAM0 */ +		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" +		/* Shutdown CPU */ +		"	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + +		"	b	.\n\t" +		: +		: "r" (mpddrc_base), +		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), +		  "r" (at91_shdwc->at91_shdwc_base), +		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) +		: "r0"); +} +  static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,  				      u32 in_period_us)  { @@ -212,6 +239,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)  {  	struct resource *res;  	const struct of_device_id *match; +	struct device_node *np; +	u32 ddr_type;  	int ret;  	if (!pdev->dev.of_node) @@ -249,6 +278,23 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)  	pm_power_off = at91_poweroff; +	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); +	if (!np) +		return 0; + +	mpddrc_base = of_iomap(np, 0); +	of_node_put(np); + +	if (!mpddrc_base) +		return 0; + +	ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; +	if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || +	    (ddr_type == AT91_DDRSDRC_MD_LPDDR3)) +		pm_power_off = at91_lpddr_poweroff; +	else +		iounmap(mpddrc_base); +  	return 0;  } @@ -256,7 +302,8 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev)  {  	struct shdwc *shdw = platform_get_drvdata(pdev); -	if (pm_power_off == at91_poweroff) +	if (pm_power_off == at91_poweroff || +	    pm_power_off == at91_lpddr_poweroff)  		pm_power_off = NULL;  	/* Reset values to disable wake-up features  */ diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 76806a0be820..da54ac88f068 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -164,6 +164,12 @@ config BATTERY_SBS  	  Say Y to include support for SBS battery driver for SBS-compliant  	  gas gauges. +config CHARGER_SBS +        tristate "SBS Compliant charger" +        depends on I2C +        help +	  Say Y to include support for SBS compilant battery chargers. +  config BATTERY_BQ27XXX  	tristate "BQ27xxx battery driver"  	help @@ -214,6 +220,18 @@ config BATTERY_DA9150  	  This driver can also be built as a module. If so, the module will be  	  called da9150-fg. +config CHARGER_AXP20X +	tristate "X-Powers AXP20X and AXP22X AC power supply driver" +	depends on MFD_AXP20X +	depends on AXP20X_ADC +	depends on IIO +	help +	  Say Y here to enable support for X-Powers AXP20X and AXP22X PMICs' AC +	  power supply. + +	  This driver can also be built as a module. If so, the module will be +	  called axp20x_ac_power. +  config AXP288_CHARGER  	tristate "X-Powers AXP288 Charger"  	depends on MFD_AXP20X && EXTCON_AXP288 @@ -292,13 +310,6 @@ config BATTERY_JZ4740  	  This driver can be build as a module. If so, the module will be  	  called jz4740-battery. -config BATTERY_INTEL_MID -	tristate "Battery driver for Intel MID platforms" -	depends on INTEL_SCU_IPC && SPI -	help -	  Say Y here to enable the battery driver on Intel MID -	  platforms. -  config BATTERY_RX51  	tristate "Nokia RX-51 (N900) battery driver"  	depends on TWL4030_MADC @@ -370,6 +381,16 @@ config CHARGER_MAX14577  	  Say Y to enable support for the battery charger control sysfs and  	  platform data of MAX14577/77836 MUICs. +config CHARGER_DETECTOR_MAX14656 +	tristate "Maxim MAX14656 USB charger detector" +	depends on I2C +	depends on OF +	help +	  Say Y to enable support for the Maxim MAX14656 USB charger detector. +	  The device is compliant with the USB Battery Charging Specification +	  Revision 1.2 and can be found e.g. in Kindle 4/5th generation +	  readers and certain LG devices. +  config CHARGER_MAX77693  	tristate "Maxim MAX77693 battery charger driver"  	depends on MFD_MAX77693 @@ -395,6 +416,7 @@ config CHARGER_QCOM_SMBB  	depends on MFD_SPMI_PMIC || COMPILE_TEST  	depends on OF  	depends on EXTCON +	depends on REGULATOR  	help  	  Say Y to include support for the Switch-Mode Battery Charger and  	  Boost (SMBB) hardware found in Qualcomm PM8941 PMICs.  The charger diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 36c599d9a495..3789a2c06fdf 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_TEST_POWER)	+= test_power.o  obj-$(CONFIG_BATTERY_88PM860X)	+= 88pm860x_battery.o  obj-$(CONFIG_BATTERY_ACT8945A)	+= act8945a_charger.o +obj-$(CONFIG_CHARGER_AXP20X)	+= axp20x_ac_power.o  obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o  obj-$(CONFIG_BATTERY_DS2780)	+= ds2780_battery.o  obj-$(CONFIG_BATTERY_DS2781)	+= ds2781_battery.o @@ -31,6 +32,7 @@ obj-$(CONFIG_BATTERY_COLLIE)	+= collie_battery.o  obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o  obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o  obj-$(CONFIG_BATTERY_SBS)	+= sbs-battery.o +obj-$(CONFIG_CHARGER_SBS)	+= sbs-charger.o  obj-$(CONFIG_BATTERY_BQ27XXX)	+= bq27xxx_battery.o  obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o  obj-$(CONFIG_BATTERY_DA9030)	+= da9030_battery.o @@ -47,7 +49,6 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC)	+= twl4030_madc_battery.o  obj-$(CONFIG_CHARGER_88PM860X)	+= 88pm860x_charger.o  obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o  obj-$(CONFIG_BATTERY_JZ4740)	+= jz4740-battery.o -obj-$(CONFIG_BATTERY_INTEL_MID)	+= intel_mid_battery.o  obj-$(CONFIG_BATTERY_RX51)	+= rx51_battery.o  obj-$(CONFIG_AB8500_BM)		+= ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o  obj-$(CONFIG_CHARGER_ISP1704)	+= isp1704_charger.o @@ -58,6 +59,7 @@ obj-$(CONFIG_CHARGER_LP8788)	+= lp8788-charger.o  obj-$(CONFIG_CHARGER_GPIO)	+= gpio-charger.o  obj-$(CONFIG_CHARGER_MANAGER)	+= charger-manager.o  obj-$(CONFIG_CHARGER_MAX14577)	+= max14577_charger.o +obj-$(CONFIG_CHARGER_DETECTOR_MAX14656)	+= max14656_charger_detector.o  obj-$(CONFIG_CHARGER_MAX77693)	+= max77693_charger.o  obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o  obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 6ffdc18f2599..f7a35ebfbab2 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -76,8 +76,8 @@ struct ab8500_btemp_ranges {   * @dev:		Pointer to the structure device   * @node:		List of AB8500 BTEMPs, hence prepared for reentrance   * @curr_source:	What current source we use, in uA - * @bat_temp:		Dispatched battery temperature in degree Celcius - * @prev_bat_temp	Last measured battery temperature in degree Celcius + * @bat_temp:		Dispatched battery temperature in degree Celsius + * @prev_bat_temp	Last measured battery temperature in degree Celsius   * @parent:		Pointer to the struct ab8500   * @gpadc:		Pointer to the struct gpadc   * @fg:			Pointer to the struct fg @@ -123,10 +123,7 @@ static LIST_HEAD(ab8500_btemp_list);   */  struct ab8500_btemp *ab8500_btemp_get(void)  { -	struct ab8500_btemp *btemp; -	btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node); - -	return btemp; +	return list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);  }  EXPORT_SYMBOL(ab8500_btemp_get); @@ -464,13 +461,13 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)   * @tbl_size:	size of the resistance to temperature table   * @res:	resistance to calculate the temperature from   * - * This function returns the battery temperature in degrees Celcius + * This function returns the battery temperature in degrees Celsius   * based on the NTC resistance.   */  static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,  	const struct abx500_res_to_temp *tbl, int tbl_size, int res)  { -	int i, temp; +	int i;  	/*  	 * Calculate the formula for the straight line  	 * Simple interpolation if we are within @@ -488,9 +485,8 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,  			i++;  	} -	temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) * +	return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *  		(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); -	return temp;  }  /** diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c new file mode 100644 index 000000000000..38f4e87cf24d --- /dev/null +++ b/drivers/power/supply/axp20x_ac_power.c @@ -0,0 +1,253 @@ +/* + * AXP20X and AXP22X PMICs' ACIN power supply driver + * + * Copyright (C) 2016 Free Electrons + *	Quentin Schulz <quentin.schulz@free-electrons.com> + * + * 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/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/iio/consumer.h> + +#define AXP20X_PWR_STATUS_ACIN_PRESENT	BIT(7) +#define AXP20X_PWR_STATUS_ACIN_AVAIL	BIT(6) + +#define DRVNAME "axp20x-ac-power-supply" + +struct axp20x_ac_power { +	struct regmap *regmap; +	struct power_supply *supply; +	struct iio_channel *acin_v; +	struct iio_channel *acin_i; +}; + +static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) +{ +	struct axp20x_ac_power *power = devid; + +	power_supply_changed(power->supply); + +	return IRQ_HANDLED; +} + +static int axp20x_ac_power_get_property(struct power_supply *psy, +					enum power_supply_property psp, +					union power_supply_propval *val) +{ +	struct axp20x_ac_power *power = power_supply_get_drvdata(psy); +	int ret, reg; + +	switch (psp) { +	case POWER_SUPPLY_PROP_HEALTH: +		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); +		if (ret) +			return ret; + +		if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) { +			val->intval = POWER_SUPPLY_HEALTH_GOOD; +			return 0; +		} + +		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; +		return 0; + +	case POWER_SUPPLY_PROP_PRESENT: +		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); +		if (ret) +			return ret; + +		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT); +		return 0; + +	case POWER_SUPPLY_PROP_ONLINE: +		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); +		if (ret) +			return ret; + +		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL); +		return 0; + +	case POWER_SUPPLY_PROP_VOLTAGE_NOW: +		ret = iio_read_channel_processed(power->acin_v, &val->intval); +		if (ret) +			return ret; + +		/* IIO framework gives mV but Power Supply framework gives uV */ +		val->intval *= 1000; + +		return 0; + +	case POWER_SUPPLY_PROP_CURRENT_NOW: +		ret = iio_read_channel_processed(power->acin_i, &val->intval); +		if (ret) +			return ret; + +		/* IIO framework gives mA but Power Supply framework gives uA */ +		val->intval *= 1000; + +		return 0; + +	default: +		return -EINVAL; +	} + +	return -EINVAL; +} + +static enum power_supply_property axp20x_ac_power_properties[] = { +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static enum power_supply_property axp22x_ac_power_properties[] = { +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_ONLINE, +}; + +static const struct power_supply_desc axp20x_ac_power_desc = { +	.name = "axp20x-ac", +	.type = POWER_SUPPLY_TYPE_MAINS, +	.properties = axp20x_ac_power_properties, +	.num_properties = ARRAY_SIZE(axp20x_ac_power_properties), +	.get_property = axp20x_ac_power_get_property, +}; + +static const struct power_supply_desc axp22x_ac_power_desc = { +	.name = "axp22x-ac", +	.type = POWER_SUPPLY_TYPE_MAINS, +	.properties = axp22x_ac_power_properties, +	.num_properties = ARRAY_SIZE(axp22x_ac_power_properties), +	.get_property = axp20x_ac_power_get_property, +}; + +struct axp_data { +	const struct power_supply_desc	*power_desc; +	bool				acin_adc; +}; + +static const struct axp_data axp20x_data = { +	.power_desc = &axp20x_ac_power_desc, +	.acin_adc = true, +}; + +static const struct axp_data axp22x_data = { +	.power_desc = &axp22x_ac_power_desc, +	.acin_adc = false, +}; + +static int axp20x_ac_power_probe(struct platform_device *pdev) +{ +	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); +	struct power_supply_config psy_cfg = {}; +	struct axp20x_ac_power *power; +	struct axp_data *axp_data; +	static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", +		NULL }; +	int i, irq, ret; + +	if (!of_device_is_available(pdev->dev.of_node)) +		return -ENODEV; + +	if (!axp20x) { +		dev_err(&pdev->dev, "Parent drvdata not set\n"); +		return -EINVAL; +	} + +	power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); +	if (!power) +		return -ENOMEM; + +	axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev); + +	if (axp_data->acin_adc) { +		power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); +		if (IS_ERR(power->acin_v)) { +			if (PTR_ERR(power->acin_v) == -ENODEV) +				return -EPROBE_DEFER; +			return PTR_ERR(power->acin_v); +		} + +		power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i"); +		if (IS_ERR(power->acin_i)) { +			if (PTR_ERR(power->acin_i) == -ENODEV) +				return -EPROBE_DEFER; +			return PTR_ERR(power->acin_i); +		} +	} + +	power->regmap = dev_get_regmap(pdev->dev.parent, NULL); + +	platform_set_drvdata(pdev, power); + +	psy_cfg.of_node = pdev->dev.of_node; +	psy_cfg.drv_data = power; + +	power->supply = devm_power_supply_register(&pdev->dev, +						   axp_data->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; 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", +				 irq_names[i], irq); +			continue; +		} +		irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); +		ret = devm_request_any_context_irq(&pdev->dev, irq, +						   axp20x_ac_power_irq, 0, +						   DRVNAME, power); +		if (ret < 0) +			dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", +				 irq_names[i], ret); +	} + +	return 0; +} + +static const struct of_device_id axp20x_ac_power_match[] = { +	{ +		.compatible = "x-powers,axp202-ac-power-supply", +		.data = (void *)&axp20x_data, +	}, { +		.compatible = "x-powers,axp221-ac-power-supply", +		.data = (void *)&axp22x_data, +	}, { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); + +static struct platform_driver axp20x_ac_power_driver = { +	.probe = axp20x_ac_power_probe, +	.driver = { +		.name = DRVNAME, +		.of_match_table = axp20x_ac_power_match, +	}, +}; + +module_platform_driver(axp20x_ac_power_driver); + +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); +MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 6af6feb7058d..2397c482656e 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -17,10 +17,12 @@  #include <linux/mfd/axp20x.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/of_device.h>  #include <linux/platform_device.h>  #include <linux/power_supply.h>  #include <linux/regmap.h>  #include <linux/slab.h> +#include <linux/iio/consumer.h>  #define DRVNAME "axp20x-usb-power-supply" @@ -30,6 +32,8 @@  #define AXP20X_USB_STATUS_VBUS_VALID	BIT(2)  #define AXP20X_VBUS_VHOLD_uV(b)		(4000000 + (((b) >> 3) & 7) * 100000) +#define AXP20X_VBUS_VHOLD_MASK		GENMASK(5, 3) +#define AXP20X_VBUS_VHOLD_OFFSET	3  #define AXP20X_VBUS_CLIMIT_MASK		3  #define AXP20X_VBUC_CLIMIT_900mA	0  #define AXP20X_VBUC_CLIMIT_500mA	1 @@ -45,6 +49,9 @@ struct axp20x_usb_power {  	struct device_node *np;  	struct regmap *regmap;  	struct power_supply *supply; +	enum axp20x_variants axp20x_id; +	struct iio_channel *vbus_v; +	struct iio_channel *vbus_i;  };  static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) @@ -72,6 +79,20 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,  		val->intval = AXP20X_VBUS_VHOLD_uV(v);  		return 0;  	case POWER_SUPPLY_PROP_VOLTAGE_NOW: +		if (IS_ENABLED(CONFIG_AXP20X_ADC)) { +			ret = iio_read_channel_processed(power->vbus_v, +							 &val->intval); +			if (ret) +				return ret; + +			/* +			 * IIO framework gives mV but Power Supply framework +			 * gives uV. +			 */ +			val->intval *= 1000; +			return 0; +		} +  		ret = axp20x_read_variable_width(power->regmap,  						 AXP20X_VBUS_V_ADC_H, 12);  		if (ret < 0) @@ -86,12 +107,10 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,  		switch (v & AXP20X_VBUS_CLIMIT_MASK) {  		case AXP20X_VBUC_CLIMIT_100mA: -			if (of_device_is_compatible(power->np, -					"x-powers,axp202-usb-power-supply")) { -				val->intval = 100000; -			} else { +			if (power->axp20x_id == AXP221_ID)  				val->intval = -1; /* No 100mA limit */ -			} +			else +				val->intval = 100000;  			break;  		case AXP20X_VBUC_CLIMIT_500mA:  			val->intval = 500000; @@ -105,6 +124,20 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,  		}  		return 0;  	case POWER_SUPPLY_PROP_CURRENT_NOW: +		if (IS_ENABLED(CONFIG_AXP20X_ADC)) { +			ret = iio_read_channel_processed(power->vbus_i, +							 &val->intval); +			if (ret) +				return ret; + +			/* +			 * IIO framework gives mA but Power Supply framework +			 * gives uA. +			 */ +			val->intval *= 1000; +			return 0; +		} +  		ret = axp20x_read_variable_width(power->regmap,  						 AXP20X_VBUS_I_ADC_H, 12);  		if (ret < 0) @@ -130,8 +163,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,  		val->intval = POWER_SUPPLY_HEALTH_GOOD; -		if (of_device_is_compatible(power->np, -				"x-powers,axp202-usb-power-supply")) { +		if (power->axp20x_id == AXP202_ID) {  			ret = regmap_read(power->regmap,  					  AXP20X_USB_OTG_STATUS, &v);  			if (ret) @@ -155,6 +187,81 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,  	return 0;  } +static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, +					    int intval) +{ +	int val; + +	switch (intval) { +	case 4000000: +	case 4100000: +	case 4200000: +	case 4300000: +	case 4400000: +	case 4500000: +	case 4600000: +	case 4700000: +		val = (intval - 4000000) / 100000; +		return regmap_update_bits(power->regmap, +					  AXP20X_VBUS_IPSOUT_MGMT, +					  AXP20X_VBUS_VHOLD_MASK, +					  val << AXP20X_VBUS_VHOLD_OFFSET); +	default: +		return -EINVAL; +	} + +	return -EINVAL; +} + +static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, +					    int intval) +{ +	int val; + +	switch (intval) { +	case 100000: +		if (power->axp20x_id == AXP221_ID) +			return -EINVAL; +	case 500000: +	case 900000: +		val = (900000 - intval) / 400000; +		return regmap_update_bits(power->regmap, +					  AXP20X_VBUS_IPSOUT_MGMT, +					  AXP20X_VBUS_CLIMIT_MASK, val); +	default: +		return -EINVAL; +	} + +	return -EINVAL; +} + +static int axp20x_usb_power_set_property(struct power_supply *psy, +					 enum power_supply_property psp, +					 const union power_supply_propval *val) +{ +	struct axp20x_usb_power *power = power_supply_get_drvdata(psy); + +	switch (psp) { +	case POWER_SUPPLY_PROP_VOLTAGE_MIN: +		return axp20x_usb_power_set_voltage_min(power, val->intval); + +	case POWER_SUPPLY_PROP_CURRENT_MAX: +		return axp20x_usb_power_set_current_max(power, val->intval); + +	default: +		return -EINVAL; +	} + +	return -EINVAL; +} + +static int axp20x_usb_power_prop_writeable(struct power_supply *psy, +					   enum power_supply_property psp) +{ +	return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || +	       psp == POWER_SUPPLY_PROP_CURRENT_MAX; +} +  static enum power_supply_property axp20x_usb_power_properties[] = {  	POWER_SUPPLY_PROP_HEALTH,  	POWER_SUPPLY_PROP_PRESENT, @@ -178,7 +285,9 @@ static const struct power_supply_desc axp20x_usb_power_desc = {  	.type = POWER_SUPPLY_TYPE_USB,  	.properties = axp20x_usb_power_properties,  	.num_properties = ARRAY_SIZE(axp20x_usb_power_properties), +	.property_is_writeable = axp20x_usb_power_prop_writeable,  	.get_property = axp20x_usb_power_get_property, +	.set_property = axp20x_usb_power_set_property,  };  static const struct power_supply_desc axp22x_usb_power_desc = { @@ -186,9 +295,41 @@ static const struct power_supply_desc axp22x_usb_power_desc = {  	.type = POWER_SUPPLY_TYPE_USB,  	.properties = axp22x_usb_power_properties,  	.num_properties = ARRAY_SIZE(axp22x_usb_power_properties), +	.property_is_writeable = axp20x_usb_power_prop_writeable,  	.get_property = axp20x_usb_power_get_property, +	.set_property = axp20x_usb_power_set_property,  }; +static int configure_iio_channels(struct platform_device *pdev, +				  struct axp20x_usb_power *power) +{ +	power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); +	if (IS_ERR(power->vbus_v)) { +		if (PTR_ERR(power->vbus_v) == -ENODEV) +			return -EPROBE_DEFER; +		return PTR_ERR(power->vbus_v); +	} + +	power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i"); +	if (IS_ERR(power->vbus_i)) { +		if (PTR_ERR(power->vbus_i) == -ENODEV) +			return -EPROBE_DEFER; +		return PTR_ERR(power->vbus_i); +	} + +	return 0; +} + +static int configure_adc_registers(struct axp20x_usb_power *power) +{ +	/* Enable vbus voltage and current measurement */ +	return 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); +} +  static int axp20x_usb_power_probe(struct platform_device *pdev)  {  	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); @@ -214,11 +355,13 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)  	if (!power)  		return -ENOMEM; +	power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( +								&pdev->dev); +  	power->np = pdev->dev.of_node;  	power->regmap = axp20x->regmap; -	if (of_device_is_compatible(power->np, -			"x-powers,axp202-usb-power-supply")) { +	if (power->axp20x_id == AXP202_ID) {  		/* Enable vbus valid checking */  		ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,  					 AXP20X_VBUS_MON_VBUS_VALID, @@ -226,17 +369,18 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)  		if (ret)  			return ret; -		/* 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 (IS_ENABLED(CONFIG_AXP20X_ADC)) +			ret = configure_iio_channels(pdev, power); +		else +			ret = configure_adc_registers(power); +  		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")) { +	} else if (power->axp20x_id == AXP221_ID || +		   power->axp20x_id == AXP223_ID) {  		usb_power_desc = &axp22x_usb_power_desc;  		irq_names = axp22x_irq_names;  	} else { @@ -273,9 +417,16 @@ 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" }, -	{ } +	{ +		.compatible = "x-powers,axp202-usb-power-supply", +		.data = (void *)AXP202_ID, +	}, { +		.compatible = "x-powers,axp221-usb-power-supply", +		.data = (void *)AXP221_ID, +	}, { +		.compatible = "x-powers,axp223-usb-power-supply", +		.data = (void *)AXP223_ID, +	}, { /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 75b8e0c7402b..6be2fe27bb07 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -90,20 +90,6 @@  #define CHRG_VLTFC_0C			0xA5	/* 0 DegC */  #define CHRG_VHTFC_45C			0x1F	/* 45 DegC */ -#define BAT_IRQ_CFG_CHRG_DONE		(1 << 2) -#define BAT_IRQ_CFG_CHRG_START		(1 << 3) -#define BAT_IRQ_CFG_BAT_SAFE_EXIT	(1 << 4) -#define BAT_IRQ_CFG_BAT_SAFE_ENTER	(1 << 5) -#define BAT_IRQ_CFG_BAT_DISCON		(1 << 6) -#define BAT_IRQ_CFG_BAT_CONN		(1 << 7) -#define BAT_IRQ_CFG_BAT_MASK		0xFC - -#define TEMP_IRQ_CFG_QCBTU		(1 << 4) -#define TEMP_IRQ_CFG_CBTU		(1 << 5) -#define TEMP_IRQ_CFG_QCBTO		(1 << 6) -#define TEMP_IRQ_CFG_CBTO		(1 << 7) -#define TEMP_IRQ_CFG_MASK		0xF0 -  #define FG_CNTL_OCV_ADJ_EN		(1 << 3)  #define CV_4100MV			4100	/* 4100mV */ @@ -127,6 +113,10 @@  #define ILIM_3000MA			3000	/* 3000mA */  #define AXP288_EXTCON_DEV_NAME		"axp288_extcon" +#define USB_HOST_EXTCON_DEV_NAME	"INT3496:00" + +static const unsigned int cable_ids[] = +	{ EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP };  enum {  	VBUS_OV_IRQ = 0, @@ -143,7 +133,6 @@ enum {  struct axp288_chrg_info {  	struct platform_device *pdev; -	struct axp20x_chrg_pdata *pdata;  	struct regmap *regmap;  	struct regmap_irq_chip_data *regmap_irqc;  	int irq[CHRG_INTR_END]; @@ -163,20 +152,16 @@ struct axp288_chrg_info {  		struct extcon_dev *edev;  		bool connected;  		enum power_supply_type chg_type; -		struct notifier_block nb; +		struct notifier_block nb[ARRAY_SIZE(cable_ids)];  		struct work_struct work;  	} cable; -	int health;  	int inlmt;  	int cc;  	int cv;  	int max_cc;  	int max_cv; -	bool online; -	bool present; -	bool enable_charger; -	bool is_charger_enabled; +	int is_charger_enabled;  };  static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -305,6 +290,9 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,  {  	int ret; +	if ((int)enable == info->is_charger_enabled) +		return 0; +  	if (enable)  		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,  				CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN); @@ -430,8 +418,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,  		ret = axp288_charger_is_present(info);  		if (ret < 0)  			goto psy_get_prop_fail; -		info->present = ret; -		val->intval = info->present; +		val->intval = ret;  		break;  	case POWER_SUPPLY_PROP_ONLINE:  		/* Check for OTG case first */ @@ -442,8 +429,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,  		ret = axp288_charger_is_online(info);  		if (ret < 0)  			goto psy_get_prop_fail; -		info->online = ret; -		val->intval = info->online; +		val->intval = ret;  		break;  	case POWER_SUPPLY_PROP_HEALTH:  		val->intval = axp288_get_charger_health(info); @@ -576,20 +562,20 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)  	struct axp288_chrg_info *info =  	    container_of(work, struct axp288_chrg_info, cable.work);  	int ret, current_limit; -	bool changed = false;  	struct extcon_dev *edev = info->cable.edev;  	bool old_connected = info->cable.connected; +	enum power_supply_type old_chg_type = info->cable.chg_type;  	/* Determine cable/charger type */ -	if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) { +	if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {  		dev_dbg(&info->pdev->dev, "USB SDP charger  is connected");  		info->cable.connected = true;  		info->cable.chg_type = POWER_SUPPLY_TYPE_USB; -	} else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) { +	} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {  		dev_dbg(&info->pdev->dev, "USB CDP charger is connected");  		info->cable.connected = true;  		info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP; -	} else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) { +	} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {  		dev_dbg(&info->pdev->dev, "USB DCP charger is connected");  		info->cable.connected = true;  		info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP; @@ -601,22 +587,15 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)  	}  	/* Cable status changed */ -	if (old_connected != info->cable.connected) -		changed = true; - -	if (!changed) +	if (old_connected == info->cable.connected && +	    old_chg_type == info->cable.chg_type)  		return;  	mutex_lock(&info->lock); -	if (info->is_charger_enabled && !info->cable.connected) { -		info->enable_charger = false; -		ret = axp288_charger_enable_charger(info, info->enable_charger); -		if (ret < 0) -			dev_err(&info->pdev->dev, -				"cannot disable charger (%d)", ret); +	if (info->cable.connected) { +		axp288_charger_enable_charger(info, false); -	} else if (!info->is_charger_enabled && info->cable.connected) {  		switch (info->cable.chg_type) {  		case POWER_SUPPLY_TYPE_USB:  			current_limit = ILIM_500MA; @@ -635,36 +614,49 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)  		/* Set vbus current limit first, then enable charger */  		ret = axp288_charger_set_vbus_inlmt(info, current_limit); -		if (ret < 0) { +		if (ret == 0) +			axp288_charger_enable_charger(info, true); +		else  			dev_err(&info->pdev->dev,  				"error setting current limit (%d)", ret); -		} else { -			info->enable_charger = (current_limit > 0); -			ret = axp288_charger_enable_charger(info, -							info->enable_charger); -			if (ret < 0) -				dev_err(&info->pdev->dev, -					"cannot enable charger (%d)", ret); -		} +	} else { +		axp288_charger_enable_charger(info, false);  	} -	if (changed) -		info->health = axp288_get_charger_health(info); -  	mutex_unlock(&info->lock); -	if (changed) -		power_supply_changed(info->psy_usb); +	power_supply_changed(info->psy_usb);  } -static int axp288_charger_handle_cable_evt(struct notifier_block *nb, -					  unsigned long event, void *param) +/* + * We need 3 copies of this, because there is no way to find out for which + * cable id we are being called from the passed in arguments; and we must + * have a separate nb for each extcon_register_notifier call. + */ +static int axp288_charger_handle_cable0_evt(struct notifier_block *nb, +					    unsigned long event, void *param)  {  	struct axp288_chrg_info *info = -	    container_of(nb, struct axp288_chrg_info, cable.nb); +		container_of(nb, struct axp288_chrg_info, cable.nb[0]); +	schedule_work(&info->cable.work); +	return NOTIFY_OK; +} +static int axp288_charger_handle_cable1_evt(struct notifier_block *nb, +					    unsigned long event, void *param) +{ +	struct axp288_chrg_info *info = +		container_of(nb, struct axp288_chrg_info, cable.nb[1]);  	schedule_work(&info->cable.work); +	return NOTIFY_OK; +} +static int axp288_charger_handle_cable2_evt(struct notifier_block *nb, +					    unsigned long event, void *param) +{ +	struct axp288_chrg_info *info = +		container_of(nb, struct axp288_chrg_info, cable.nb[2]); +	schedule_work(&info->cable.work);  	return NOTIFY_OK;  } @@ -672,7 +664,17 @@ static void axp288_charger_otg_evt_worker(struct work_struct *work)  {  	struct axp288_chrg_info *info =  	    container_of(work, struct axp288_chrg_info, otg.work); -	int ret; +	struct extcon_dev *edev = info->otg.cable; +	int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST); + +	dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", +				usb_host ? "attached" : "detached"); + +	/* +	 * Set usb_id_short flag to avoid running charger detection logic +	 * in case usb host. +	 */ +	info->otg.id_short = usb_host;  	/* Disable VBUS path before enabling the 5V boost */  	ret = axp288_charger_vbus_path_select(info, !info->otg.id_short); @@ -685,135 +687,109 @@ static int axp288_charger_handle_otg_evt(struct notifier_block *nb,  {  	struct axp288_chrg_info *info =  	    container_of(nb, struct axp288_chrg_info, otg.id_nb); -	struct extcon_dev *edev = info->otg.cable; -	int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST); -	dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", -				usb_host ? "attached" : "detached"); - -	/* -	 * Set usb_id_short flag to avoid running charger detection logic -	 * in case usb host. -	 */ -	info->otg.id_short = usb_host;  	schedule_work(&info->otg.work);  	return NOTIFY_OK;  } -static void charger_init_hw_regs(struct axp288_chrg_info *info) +static int charger_init_hw_regs(struct axp288_chrg_info *info)  {  	int ret, cc, cv;  	unsigned int val;  	/* Program temperature thresholds */  	ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", +	if (ret < 0) { +		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",  							AXP20X_V_LTF_CHRG, ret); +		return ret; +	}  	ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", +	if (ret < 0) { +		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",  							AXP20X_V_HTF_CHRG, ret); +		return ret; +	}  	/* Do not turn-off charger o/p after charge cycle ends */  	ret = regmap_update_bits(info->regmap,  				AXP20X_CHRG_CTRL2, -				CNTL2_CHG_OUT_TURNON, 1); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", +				CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON); +	if (ret < 0) { +		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",  						AXP20X_CHRG_CTRL2, ret); - -	/* Enable interrupts */ -	ret = regmap_update_bits(info->regmap, -				AXP20X_IRQ2_EN, -				BAT_IRQ_CFG_BAT_MASK, 1); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", -						AXP20X_IRQ2_EN, ret); - -	ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN, -				TEMP_IRQ_CFG_MASK, 1); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", -						AXP20X_IRQ3_EN, ret); +		return ret; +	}  	/* Setup ending condition for charging to be 10% of I(chrg) */  	ret = regmap_update_bits(info->regmap,  				AXP20X_CHRG_CTRL1,  				CHRG_CCCV_ITERM_20P, 0); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", +	if (ret < 0) { +		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",  						AXP20X_CHRG_CTRL1, ret); +		return ret; +	}  	/* Disable OCV-SOC curve calibration */  	ret = regmap_update_bits(info->regmap,  				AXP20X_CC_CTRL,  				FG_CNTL_OCV_ADJ_EN, 0); -	if (ret < 0) -		dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n", +	if (ret < 0) { +		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",  						AXP20X_CC_CTRL, ret); - -	/* Init charging current and voltage */ -	info->max_cc = info->pdata->max_cc; -	info->max_cv = info->pdata->max_cv; +		return ret; +	}  	/* Read current charge voltage and current limit */  	ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);  	if (ret < 0) { -		/* Assume default if cannot read */ -		info->cc = info->pdata->def_cc; -		info->cv = info->pdata->def_cv; -	} else { -		/* Determine charge voltage */ -		cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS; -		switch (cv) { -		case CHRG_CCCV_CV_4100MV: -			info->cv = CV_4100MV; -			break; -		case CHRG_CCCV_CV_4150MV: -			info->cv = CV_4150MV; -			break; -		case CHRG_CCCV_CV_4200MV: -			info->cv = CV_4200MV; -			break; -		case CHRG_CCCV_CV_4350MV: -			info->cv = CV_4350MV; -			break; -		default: -			info->cv = INT_MAX; -			break; -		} +		dev_err(&info->pdev->dev, "register(%x) read error(%d)\n", +			AXP20X_CHRG_CTRL1, ret); +		return ret; +	} -		/* Determine charge current limit */ -		cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS; -		cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET; -		info->cc = cc; +	/* Determine charge voltage */ +	cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS; +	switch (cv) { +	case CHRG_CCCV_CV_4100MV: +		info->cv = CV_4100MV; +		break; +	case CHRG_CCCV_CV_4150MV: +		info->cv = CV_4150MV; +		break; +	case CHRG_CCCV_CV_4200MV: +		info->cv = CV_4200MV; +		break; +	case CHRG_CCCV_CV_4350MV: +		info->cv = CV_4350MV; +		break; +	} -		/* Program default charging voltage and current */ -		cc = min(info->pdata->def_cc, info->max_cc); -		cv = min(info->pdata->def_cv, info->max_cv); +	/* Determine charge current limit */ +	cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS; +	cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET; +	info->cc = cc; -		ret = axp288_charger_set_cc(info, cc); -		if (ret < 0) -			dev_warn(&info->pdev->dev, -					"error(%d) in setting CC\n", ret); +	/* +	 * Do not allow the user to configure higher settings then those +	 * set by the firmware +	 */ +	info->max_cv = info->cv; +	info->max_cc = info->cc; -		ret = axp288_charger_set_cv(info, cv); -		if (ret < 0) -			dev_warn(&info->pdev->dev, -					"error(%d) in setting CV\n", ret); -	} +	return 0;  }  static int axp288_charger_probe(struct platform_device *pdev)  {  	int ret, i, pirq;  	struct axp288_chrg_info *info; +	struct device *dev = &pdev->dev;  	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);  	struct power_supply_config charger_cfg = {}; -  	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);  	if (!info)  		return -ENOMEM; @@ -821,15 +797,8 @@ static int axp288_charger_probe(struct platform_device *pdev)  	info->pdev = pdev;  	info->regmap = axp20x->regmap;  	info->regmap_irqc = axp20x->regmap_irqc; -	info->pdata = pdev->dev.platform_data; - -	if (!info->pdata) { -		/* Try ACPI provided pdata via device properties */ -		if (!device_property_present(&pdev->dev, -						"axp288_charger_data\n")) -			dev_err(&pdev->dev, "failed to get platform data\n"); -		return -ENODEV; -	} +	info->cable.chg_type = -1; +	info->is_charger_enabled = -1;  	info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);  	if (info->cable.edev == NULL) { @@ -838,63 +807,55 @@ static int axp288_charger_probe(struct platform_device *pdev)  		return -EPROBE_DEFER;  	} -	/* Register for extcon notification */ -	INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); -	info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; -	ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, -					&info->cable.nb); -	if (ret) { -		dev_err(&info->pdev->dev, -			"failed to register extcon notifier for SDP %d\n", ret); -		return ret; -	} - -	ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, -					&info->cable.nb); -	if (ret) { -		dev_err(&info->pdev->dev, -			"failed to register extcon notifier for CDP %d\n", ret); -		extcon_unregister_notifier(info->cable.edev, -				EXTCON_CHG_USB_SDP, &info->cable.nb); -		return ret; -	} - -	ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, -					&info->cable.nb); -	if (ret) { -		dev_err(&info->pdev->dev, -			"failed to register extcon notifier for DCP %d\n", ret); -		extcon_unregister_notifier(info->cable.edev, -				EXTCON_CHG_USB_SDP, &info->cable.nb); -		extcon_unregister_notifier(info->cable.edev, -				EXTCON_CHG_USB_CDP, &info->cable.nb); -		return ret; +	info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_DEV_NAME); +	if (info->otg.cable == NULL) { +		dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n"); +		return -EPROBE_DEFER;  	}  	platform_set_drvdata(pdev, info);  	mutex_init(&info->lock); +	ret = charger_init_hw_regs(info); +	if (ret) +		return ret; +  	/* Register with power supply class */  	charger_cfg.drv_data = info; -	info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc, -						&charger_cfg); +	info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc, +						   &charger_cfg);  	if (IS_ERR(info->psy_usb)) { -		dev_err(&pdev->dev, "failed to register power supply charger\n");  		ret = PTR_ERR(info->psy_usb); -		goto psy_reg_failed; +		dev_err(dev, "failed to register power supply: %d\n", ret); +		return ret; +	} + +	/* Register for extcon notification */ +	INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); +	info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt; +	info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt; +	info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt; +	for (i = 0; i < ARRAY_SIZE(cable_ids); i++) { +		ret = devm_extcon_register_notifier(dev, info->cable.edev, +					  cable_ids[i], &info->cable.nb[i]); +		if (ret) { +			dev_err(dev, "failed to register extcon notifier for %u: %d\n", +				cable_ids[i], ret); +			return ret; +		}  	} +	schedule_work(&info->cable.work);  	/* Register for OTG notification */  	INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);  	info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; -	ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST, -				       &info->otg.id_nb); -	if (ret) -		dev_warn(&pdev->dev, "failed to register otg notifier\n"); - -	if (info->otg.cable) -		info->otg.id_short = extcon_get_cable_state_( -					info->otg.cable, EXTCON_USB_HOST); +	ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable, +					EXTCON_USB_HOST, &info->otg.id_nb); +	if (ret) { +		dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n"); +		return ret; +	} +	schedule_work(&info->otg.work);  	/* Register charger interrupts */  	for (i = 0; i < CHRG_INTR_END; i++) { @@ -903,8 +864,7 @@ static int axp288_charger_probe(struct platform_device *pdev)  		if (info->irq[i] < 0) {  			dev_warn(&info->pdev->dev,  				"failed to get virtual interrupt=%d\n", pirq); -			ret = info->irq[i]; -			goto intr_reg_failed; +			return info->irq[i];  		}  		ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],  					NULL, axp288_charger_irq_thread_handler, @@ -912,51 +872,22 @@ static int axp288_charger_probe(struct platform_device *pdev)  		if (ret) {  			dev_err(&pdev->dev, "failed to request interrupt=%d\n",  								info->irq[i]); -			goto intr_reg_failed; +			return ret;  		}  	} -	charger_init_hw_regs(info); -  	return 0; - -intr_reg_failed: -	if (info->otg.cable) -		extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST, -					&info->otg.id_nb); -	power_supply_unregister(info->psy_usb); -psy_reg_failed: -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, -					&info->cable.nb); -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, -					&info->cable.nb); -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, -					&info->cable.nb); -	return ret;  } -static int axp288_charger_remove(struct platform_device *pdev) -{ -	struct axp288_chrg_info *info =  dev_get_drvdata(&pdev->dev); - -	if (info->otg.cable) -		extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST, -					&info->otg.id_nb); - -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, -					&info->cable.nb); -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, -					&info->cable.nb); -	extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, -					&info->cable.nb); -	power_supply_unregister(info->psy_usb); - -	return 0; -} +static const struct platform_device_id axp288_charger_id_table[] = { +	{ .name = "axp288_charger" }, +	{}, +}; +MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);  static struct platform_driver axp288_charger_driver = {  	.probe = axp288_charger_probe, -	.remove = axp288_charger_remove, +	.id_table = axp288_charger_id_table,  	.driver = {  		.name = "axp288_charger",  	}, diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 539eb41504bb..a8dcabc32721 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -29,6 +29,7 @@  #include <linux/iio/consumer.h>  #include <linux/debugfs.h>  #include <linux/seq_file.h> +#include <asm/unaligned.h>  #define CHRG_STAT_BAT_SAFE_MODE		(1 << 3)  #define CHRG_STAT_BAT_VALID			(1 << 4) @@ -49,23 +50,6 @@  #define CHRG_CCCV_CV_4350MV			0x3     /* 4.35V */  #define CHRG_CCCV_CHG_EN			(1 << 7) -#define CV_4100						4100    /* 4100mV */ -#define CV_4150						4150    /* 4150mV */ -#define CV_4200						4200    /* 4200mV */ -#define CV_4350						4350    /* 4350mV */ - -#define TEMP_IRQ_CFG_QWBTU			(1 << 0) -#define TEMP_IRQ_CFG_WBTU			(1 << 1) -#define TEMP_IRQ_CFG_QWBTO			(1 << 2) -#define TEMP_IRQ_CFG_WBTO			(1 << 3) -#define TEMP_IRQ_CFG_MASK			0xf - -#define FG_IRQ_CFG_LOWBATT_WL2		(1 << 0) -#define FG_IRQ_CFG_LOWBATT_WL1		(1 << 1) -#define FG_IRQ_CFG_LOWBATT_MASK		0x3 -#define LOWBAT_IRQ_STAT_LOWBATT_WL2	(1 << 0) -#define LOWBAT_IRQ_STAT_LOWBATT_WL1	(1 << 1) -  #define FG_CNTL_OCV_ADJ_STAT		(1 << 2)  #define FG_CNTL_OCV_ADJ_EN			(1 << 3)  #define FG_CNTL_CAP_ADJ_STAT		(1 << 4) @@ -73,17 +57,15 @@  #define FG_CNTL_CC_EN				(1 << 6)  #define FG_CNTL_GAUGE_EN			(1 << 7) +#define FG_15BIT_WORD_VALID			(1 << 15) +#define FG_15BIT_VAL_MASK			0x7fff +  #define FG_REP_CAP_VALID			(1 << 7)  #define FG_REP_CAP_VAL_MASK			0x7F  #define FG_DES_CAP1_VALID			(1 << 7) -#define FG_DES_CAP1_VAL_MASK		0x7F -#define FG_DES_CAP0_VAL_MASK		0xFF  #define FG_DES_CAP_RES_LSB			1456    /* 1.456mAhr */ -#define FG_CC_MTR1_VALID			(1 << 7) -#define FG_CC_MTR1_VAL_MASK			0x7F -#define FG_CC_MTR0_VAL_MASK			0xFF  #define FG_DES_CC_RES_LSB			1456    /* 1.456mAhr */  #define FG_OCV_CAP_VALID			(1 << 7) @@ -104,9 +86,7 @@  /* 1.1mV per LSB expressed in uV */  #define VOLTAGE_FROM_ADC(a)			((a * 11) / 10) -/* properties converted to tenths of degrees, uV, uA, uW */ -#define PROP_TEMP(a)		((a) * 10) -#define UNPROP_TEMP(a)		((a) / 10) +/* properties converted to uV, uA */  #define PROP_VOLT(a)		((a) * 1000)  #define PROP_CURR(a)		((a) * 1000) @@ -122,13 +102,13 @@ enum {  struct axp288_fg_info {  	struct platform_device *pdev; -	struct axp20x_fg_pdata *pdata;  	struct regmap *regmap;  	struct regmap_irq_chip_data *regmap_irqc;  	int irq[AXP288_FG_INTR_NUM];  	struct power_supply *bat;  	struct mutex lock;  	int status; +	int max_volt;  	struct delayed_work status_monitor;  	struct dentry *debug_file;  }; @@ -138,22 +118,14 @@ static enum power_supply_property fuel_gauge_props[] = {  	POWER_SUPPLY_PROP_PRESENT,  	POWER_SUPPLY_PROP_HEALTH,  	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,  	POWER_SUPPLY_PROP_VOLTAGE_NOW,  	POWER_SUPPLY_PROP_VOLTAGE_OCV,  	POWER_SUPPLY_PROP_CURRENT_NOW,  	POWER_SUPPLY_PROP_CAPACITY,  	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, -	POWER_SUPPLY_PROP_TEMP, -	POWER_SUPPLY_PROP_TEMP_MAX, -	POWER_SUPPLY_PROP_TEMP_MIN, -	POWER_SUPPLY_PROP_TEMP_ALERT_MIN, -	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,  	POWER_SUPPLY_PROP_TECHNOLOGY,  	POWER_SUPPLY_PROP_CHARGE_FULL,  	POWER_SUPPLY_PROP_CHARGE_NOW, -	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -	POWER_SUPPLY_PROP_MODEL_NAME,  };  static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) @@ -169,8 +141,10 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)  			break;  	} -	if (ret < 0) +	if (ret < 0) {  		dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret); +		return ret; +	}  	return val;  } @@ -187,6 +161,44 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)  	return ret;  } +static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg) +{ +	unsigned char buf[2]; +	int ret; + +	ret = regmap_bulk_read(info->regmap, reg, buf, 2); +	if (ret < 0) { +		dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", +			reg, ret); +		return ret; +	} + +	ret = get_unaligned_be16(buf); +	if (!(ret & FG_15BIT_WORD_VALID)) { +		dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n", +			reg); +		return -ENXIO; +	} + +	return ret & FG_15BIT_VAL_MASK; +} + +static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) +{ +	unsigned char buf[2]; +	int ret; + +	ret = regmap_bulk_read(info->regmap, reg, buf, 2); +	if (ret < 0) { +		dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n", +			reg, ret); +		return ret; +	} + +	/* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */ +	return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); +} +  static int pmic_read_adc_val(const char *name, int *raw_val,  		struct axp288_fg_info *info)  { @@ -247,24 +259,15 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data)  	seq_printf(s, "    FG_RDC0[%02x] : %02x\n",  		AXP288_FG_RDC0_REG,  		fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG)); -	seq_printf(s, "    FG_OCVH[%02x] : %02x\n", +	seq_printf(s, "     FG_OCV[%02x] : %04x\n",  		AXP288_FG_OCVH_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG)); -	seq_printf(s, "    FG_OCVL[%02x] : %02x\n", -		AXP288_FG_OCVL_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG)); -	seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n", +		fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG)); +	seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",  		AXP288_FG_DES_CAP1_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG)); -	seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n", -		AXP288_FG_DES_CAP0_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG)); -	seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n", +		fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG)); +	seq_printf(s, "  FG_CC_MTR[%02x] : %04x\n",  		AXP288_FG_CC_MTR1_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG)); -	seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n", -		AXP288_FG_CC_MTR0_REG, -		fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG)); +		fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));  	seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",  		AXP288_FG_OCV_CAP_REG,  		fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG)); @@ -417,143 +420,27 @@ current_read_fail:  	return ret;  } -static int temp_to_adc(struct axp288_fg_info *info, int tval) -{ -	int rntc = 0, i, ret, adc_val; -	int rmin, rmax, tmin, tmax; -	int tcsz = info->pdata->tcsz; - -	/* get the Rntc resitance value for this temp */ -	if (tval > info->pdata->thermistor_curve[0][1]) { -		rntc = info->pdata->thermistor_curve[0][0]; -	} else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) { -		rntc = info->pdata->thermistor_curve[tcsz-1][0]; -	} else { -		for (i = 1; i < tcsz; i++) { -			if (tval > info->pdata->thermistor_curve[i][1]) { -				rmin = info->pdata->thermistor_curve[i-1][0]; -				rmax = info->pdata->thermistor_curve[i][0]; -				tmin = info->pdata->thermistor_curve[i-1][1]; -				tmax = info->pdata->thermistor_curve[i][1]; -				rntc = rmin + ((rmax - rmin) * -					(tval - tmin) / (tmax - tmin)); -				break; -			} -		} -	} - -	/* we need the current to calculate the proper adc voltage */ -	ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE); -	if (ret < 0) { -		dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); -		ret = 0x30; -	} - -	/* -	 * temperature is proportional to NTS thermistor resistance -	 * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA -	 * [12-bit ADC VAL] = R_NTC(Ω) * current / 800 -	 */ -	adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800; - -	return adc_val; -} - -static int adc_to_temp(struct axp288_fg_info *info, int adc_val) -{ -	int ret, r, i, tval = 0; -	int rmin, rmax, tmin, tmax; -	int tcsz = info->pdata->tcsz; - -	ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE); -	if (ret < 0) { -		dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); -		ret = 0x30; -	} - -	/* -	 * temperature is proportional to NTS thermistor resistance -	 * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA -	 * R_NTC(Ω) = [12-bit ADC VAL] * 800 / current -	 */ -	r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3))); - -	if (r < info->pdata->thermistor_curve[0][0]) { -		tval = info->pdata->thermistor_curve[0][1]; -	} else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) { -		tval = info->pdata->thermistor_curve[tcsz-1][1]; -	} else { -		for (i = 1; i < tcsz; i++) { -			if (r < info->pdata->thermistor_curve[i][0]) { -				rmin = info->pdata->thermistor_curve[i-1][0]; -				rmax = info->pdata->thermistor_curve[i][0]; -				tmin = info->pdata->thermistor_curve[i-1][1]; -				tmax = info->pdata->thermistor_curve[i][1]; -				tval = tmin + ((tmax - tmin) * -					(r - rmin) / (rmax - rmin)); -				break; -			} -		} -	} - -	return tval; -} - -static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp) -{ -	int ret, raw_val = 0; - -	ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info); -	if (ret < 0) -		goto temp_read_fail; - -	*btemp = adc_to_temp(info, raw_val); - -temp_read_fail: -	return ret; -} -  static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)  { -	int ret, value; - -	/* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */ -	ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG); -	if (ret < 0) -		goto vocv_read_fail; -	value = ret << 4; +	int ret; -	ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG); -	if (ret < 0) -		goto vocv_read_fail; -	value |= (ret & 0xf); +	ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG); +	if (ret >= 0) +		*vocv = VOLTAGE_FROM_ADC(ret); -	*vocv = VOLTAGE_FROM_ADC(value); -vocv_read_fail:  	return ret;  }  static int fuel_gauge_battery_health(struct axp288_fg_info *info)  { -	int temp, vocv; -	int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN; - -	ret = fuel_gauge_get_btemp(info, &temp); -	if (ret < 0) -		goto health_read_fail; +	int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;  	ret = fuel_gauge_get_vocv(info, &vocv);  	if (ret < 0)  		goto health_read_fail; -	if (vocv > info->pdata->max_volt) +	if (vocv > info->max_volt)  		health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; -	else if (temp > info->pdata->max_temp) -		health = POWER_SUPPLY_HEALTH_OVERHEAT; -	else if (temp < info->pdata->min_temp) -		health = POWER_SUPPLY_HEALTH_COLD; -	else if (vocv < info->pdata->min_volt) -		health = POWER_SUPPLY_HEALTH_DEAD;  	else  		health = POWER_SUPPLY_HEALTH_GOOD; @@ -561,28 +448,6 @@ health_read_fail:  	return health;  } -static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info) -{ -	int ret, adc_val; - -	/* program temperature threshold as 1/16 ADC value */ -	adc_val = temp_to_adc(info, info->pdata->max_temp); -	ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4); - -	return ret; -} - -static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info) -{ -	int ret, adc_val; - -	/* program temperature threshold as 1/16 ADC value */ -	adc_val = temp_to_adc(info, info->pdata->min_temp); -	ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4); - -	return ret; -} -  static int fuel_gauge_get_property(struct power_supply *ps,  		enum power_supply_property prop,  		union power_supply_propval *val) @@ -643,58 +508,25 @@ static int fuel_gauge_get_property(struct power_supply *ps,  			goto fuel_gauge_read_err;  		val->intval = (ret & 0x0f);  		break; -	case POWER_SUPPLY_PROP_TEMP: -		ret = fuel_gauge_get_btemp(info, &value); -		if (ret < 0) -			goto fuel_gauge_read_err; -		val->intval = PROP_TEMP(value); -		break; -	case POWER_SUPPLY_PROP_TEMP_MAX: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: -		val->intval = PROP_TEMP(info->pdata->max_temp); -		break; -	case POWER_SUPPLY_PROP_TEMP_MIN: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: -		val->intval = PROP_TEMP(info->pdata->min_temp); -		break;  	case POWER_SUPPLY_PROP_TECHNOLOGY:  		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;  		break;  	case POWER_SUPPLY_PROP_CHARGE_NOW: -		ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG); +		ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);  		if (ret < 0)  			goto fuel_gauge_read_err; -		value = (ret & FG_CC_MTR1_VAL_MASK) << 8; -		ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG); -		if (ret < 0) -			goto fuel_gauge_read_err; -		value |= (ret & FG_CC_MTR0_VAL_MASK); -		val->intval = value * FG_DES_CAP_RES_LSB; +		val->intval = ret * FG_DES_CAP_RES_LSB;  		break;  	case POWER_SUPPLY_PROP_CHARGE_FULL: -		ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); +		ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);  		if (ret < 0)  			goto fuel_gauge_read_err; -		value = (ret & FG_DES_CAP1_VAL_MASK) << 8; -		ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG); -		if (ret < 0) -			goto fuel_gauge_read_err; -		value |= (ret & FG_DES_CAP0_VAL_MASK); -		val->intval = value * FG_DES_CAP_RES_LSB; -		break; -	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -		val->intval = PROP_CURR(info->pdata->design_cap); +		val->intval = ret * FG_DES_CAP_RES_LSB;  		break;  	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: -		val->intval = PROP_VOLT(info->pdata->max_volt); -		break; -	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: -		val->intval = PROP_VOLT(info->pdata->min_volt); -		break; -	case POWER_SUPPLY_PROP_MODEL_NAME: -		val->strval = info->pdata->battid; +		val->intval = PROP_VOLT(info->max_volt);  		break;  	default:  		mutex_unlock(&info->lock); @@ -718,35 +550,6 @@ static int fuel_gauge_set_property(struct power_supply *ps,  	mutex_lock(&info->lock);  	switch (prop) { -	case POWER_SUPPLY_PROP_STATUS: -		info->status = val->intval; -		break; -	case POWER_SUPPLY_PROP_TEMP_MIN: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: -		if ((val->intval < PD_DEF_MIN_TEMP) || -			(val->intval > PD_DEF_MAX_TEMP)) { -			ret = -EINVAL; -			break; -		} -		info->pdata->min_temp = UNPROP_TEMP(val->intval); -		ret = fuel_gauge_set_low_btemp_alert(info); -		if (ret < 0) -			dev_err(&info->pdev->dev, -				"temp alert min set fail:%d\n", ret); -		break; -	case POWER_SUPPLY_PROP_TEMP_MAX: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: -		if ((val->intval < PD_DEF_MIN_TEMP) || -			(val->intval > PD_DEF_MAX_TEMP)) { -			ret = -EINVAL; -			break; -		} -		info->pdata->max_temp = UNPROP_TEMP(val->intval); -		ret = fuel_gauge_set_high_btemp_alert(info); -		if (ret < 0) -			dev_err(&info->pdev->dev, -				"temp alert max set fail:%d\n", ret); -		break;  	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:  		if ((val->intval < 0) || (val->intval > 15)) {  			ret = -EINVAL; @@ -774,11 +577,6 @@ static int fuel_gauge_property_is_writeable(struct power_supply *psy,  	int ret;  	switch (psp) { -	case POWER_SUPPLY_PROP_STATUS: -	case POWER_SUPPLY_PROP_TEMP_MIN: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: -	case POWER_SUPPLY_PROP_TEMP_MAX: -	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:  	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:  		ret = 1;  		break; @@ -863,158 +661,6 @@ static const struct power_supply_desc fuel_gauge_desc = {  	.external_power_changed	= fuel_gauge_external_power_changed,  }; -static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info) -{ -	int ret; -	u8 reg_val; - -	ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); -	if (ret < 0) { -		dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); -		return ret; -	} -	ret = (ret & FG_REP_CAP_VAL_MASK); - -	if (ret > FG_LOW_CAP_WARN_THR) -		reg_val = FG_LOW_CAP_WARN_THR; -	else if (ret > FG_LOW_CAP_CRIT_THR) -		reg_val = FG_LOW_CAP_CRIT_THR; -	else -		reg_val = FG_LOW_CAP_SHDN_THR; - -	reg_val |= FG_LOW_CAP_THR1_VAL; -	ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val); -	if (ret < 0) -		dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret); - -	return ret; -} - -static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info) -{ -	int ret; -	u8 val; - -	ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); -	if (ret < 0) -		goto fg_prog_ocv_fail; -	else -		val = (ret & ~CHRG_CCCV_CV_MASK); - -	switch (info->pdata->max_volt) { -	case CV_4100: -		val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS); -		break; -	case CV_4150: -		val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS); -		break; -	case CV_4200: -		val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS); -		break; -	case CV_4350: -		val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS); -		break; -	default: -		val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS); -		break; -	} - -	ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val); -fg_prog_ocv_fail: -	return ret; -} - -static int fuel_gauge_program_design_cap(struct axp288_fg_info *info) -{ -	int ret; - -	ret = fuel_gauge_reg_writeb(info, -		AXP288_FG_DES_CAP1_REG, info->pdata->cap1); -	if (ret < 0) -		goto fg_prog_descap_fail; - -	ret = fuel_gauge_reg_writeb(info, -		AXP288_FG_DES_CAP0_REG, info->pdata->cap0); - -fg_prog_descap_fail: -	return ret; -} - -static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info) -{ -	int ret = 0, i; - -	for (i = 0; i < OCV_CURVE_SIZE; i++) { -		ret = fuel_gauge_reg_writeb(info, -			AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]); -		if (ret < 0) -			goto fg_prog_ocv_fail; -	} - -fg_prog_ocv_fail: -	return ret; -} - -static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info) -{ -	int ret; - -	ret = fuel_gauge_reg_writeb(info, -		AXP288_FG_RDC1_REG, info->pdata->rdc1); -	if (ret < 0) -		goto fg_prog_ocv_fail; - -	ret = fuel_gauge_reg_writeb(info, -		AXP288_FG_RDC0_REG, info->pdata->rdc0); - -fg_prog_ocv_fail: -	return ret; -} - -static void fuel_gauge_init_config_regs(struct axp288_fg_info *info) -{ -	int ret; - -	/* -	 * check if the config data is already -	 * programmed and if so just return. -	 */ - -	ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); -	if (ret < 0) { -		dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n"); -	} else if (!(ret & FG_DES_CAP1_VALID)) { -		dev_info(&info->pdev->dev, "FG data needs to be initialized\n"); -	} else { -		dev_info(&info->pdev->dev, "FG data is already initialized\n"); -		return; -	} - -	ret = fuel_gauge_program_vbatt_full(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret); - -	ret = fuel_gauge_program_design_cap(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret); - -	ret = fuel_gauge_program_rdc_vals(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret); - -	ret = fuel_gauge_program_ocv_curve(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret); - -	ret = fuel_gauge_set_lowbatt_thresholds(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret); - -	ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef); -	if (ret < 0) -		dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret); -} -  static void fuel_gauge_init_irq(struct axp288_fg_info *info)  {  	int ret, i, pirq; @@ -1052,29 +698,6 @@ intr_failed:  	}  } -static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info) -{ -	int ret; -	unsigned int val; - -	ret = fuel_gauge_set_high_btemp_alert(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret); - -	ret = fuel_gauge_set_low_btemp_alert(info); -	if (ret < 0) -		dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret); - -	/* enable interrupts */ -	val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN); -	val |= TEMP_IRQ_CFG_MASK; -	fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val); - -	val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN); -	val |= FG_IRQ_CFG_LOWBATT_MASK; -	val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val); -} -  static int axp288_fuel_gauge_probe(struct platform_device *pdev)  {  	int ret = 0; @@ -1090,15 +713,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)  	info->regmap = axp20x->regmap;  	info->regmap_irqc = axp20x->regmap_irqc;  	info->status = POWER_SUPPLY_STATUS_UNKNOWN; -	info->pdata = pdev->dev.platform_data; -	if (!info->pdata) -		return -ENODEV;  	platform_set_drvdata(pdev, info);  	mutex_init(&info->lock);  	INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor); +	ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); +	if (ret < 0) +		return ret; + +	if (!(ret & FG_DES_CAP1_VALID)) { +		dev_err(&pdev->dev, "axp288 not configured by firmware\n"); +		return -ENODEV; +	} + +	ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); +	if (ret < 0) +		return ret; +	switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { +	case CHRG_CCCV_CV_4100MV: +		info->max_volt = 4100; +		break; +	case CHRG_CCCV_CV_4150MV: +		info->max_volt = 4150; +		break; +	case CHRG_CCCV_CV_4200MV: +		info->max_volt = 4200; +		break; +	case CHRG_CCCV_CV_4350MV: +		info->max_volt = 4350; +		break; +	} +  	psy_cfg.drv_data = info;  	info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);  	if (IS_ERR(info->bat)) { @@ -1108,12 +755,10 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)  	}  	fuel_gauge_create_debugfs(info); -	fuel_gauge_init_config_regs(info);  	fuel_gauge_init_irq(info); -	fuel_gauge_init_hw_regs(info);  	schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); -	return ret; +	return 0;  }  static const struct platform_device_id axp288_fg_id_table[] = { diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index 73e2f0b79dd4..c4770a94cc8e 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -1569,6 +1569,11 @@ static int bq2415x_probe(struct i2c_client *client,  		acpi_id =  			acpi_match_device(client->dev.driver->acpi_match_table,  					  &client->dev); +		if (!acpi_id) { +			dev_err(&client->dev, "failed to match device name\n"); +			ret = -ENODEV; +			goto error_1; +		}  		name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);  	}  	if (!name) { diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index e9584330aeed..a4f08492abeb 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -144,10 +144,7 @@   * so the first read after a fault returns the latched value and subsequent   * reads return the current value.  In order to return the fault status   * to the user, have the interrupt handler save the reg's value and retrieve - * it in the appropriate health/status routine.  Each routine has its own - * flag indicating whether it should use the value stored by the last run - * of the interrupt handler or do an actual reg read.  That way each routine - * can report back whatever fault may have occured. + * it in the appropriate health/status routine.   */  struct bq24190_dev_info {  	struct i2c_client		*client; @@ -159,10 +156,6 @@ struct bq24190_dev_info {  	unsigned int			gpio_int;  	unsigned int			irq;  	struct mutex			f_reg_lock; -	bool				first_time; -	bool				charger_health_valid; -	bool				battery_health_valid; -	bool				battery_status_valid;  	u8				f_reg;  	u8				ss_reg;  	u8				watchdog; @@ -199,7 +192,7 @@ static const int bq24190_cvc_vreg_values[] = {  	4400000  }; -/* REG06[1:0] (TREG) in tenths of degrees Celcius */ +/* REG06[1:0] (TREG) in tenths of degrees Celsius */  static const int bq24190_ictrc_treg_values[] = {  	600, 800, 1000, 1200  }; @@ -636,21 +629,11 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,  		union power_supply_propval *val)  {  	u8 v; -	int health, ret; +	int health;  	mutex_lock(&bdi->f_reg_lock); - -	if (bdi->charger_health_valid) { -		v = bdi->f_reg; -		bdi->charger_health_valid = false; -		mutex_unlock(&bdi->f_reg_lock); -	} else { -		mutex_unlock(&bdi->f_reg_lock); - -		ret = bq24190_read(bdi, BQ24190_REG_F, &v); -		if (ret < 0) -			return ret; -	} +	v = bdi->f_reg; +	mutex_unlock(&bdi->f_reg_lock);  	if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {  		/* @@ -937,18 +920,8 @@ static int bq24190_battery_get_status(struct bq24190_dev_info *bdi,  	int status, ret;  	mutex_lock(&bdi->f_reg_lock); - -	if (bdi->battery_status_valid) { -		chrg_fault = bdi->f_reg; -		bdi->battery_status_valid = false; -		mutex_unlock(&bdi->f_reg_lock); -	} else { -		mutex_unlock(&bdi->f_reg_lock); - -		ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault); -		if (ret < 0) -			return ret; -	} +	chrg_fault = bdi->f_reg; +	mutex_unlock(&bdi->f_reg_lock);  	chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;  	chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; @@ -996,21 +969,11 @@ static int bq24190_battery_get_health(struct bq24190_dev_info *bdi,  		union power_supply_propval *val)  {  	u8 v; -	int health, ret; +	int health;  	mutex_lock(&bdi->f_reg_lock); - -	if (bdi->battery_health_valid) { -		v = bdi->f_reg; -		bdi->battery_health_valid = false; -		mutex_unlock(&bdi->f_reg_lock); -	} else { -		mutex_unlock(&bdi->f_reg_lock); - -		ret = bq24190_read(bdi, BQ24190_REG_F, &v); -		if (ret < 0) -			return ret; -	} +	v = bdi->f_reg; +	mutex_unlock(&bdi->f_reg_lock);  	if (v & BQ24190_REG_F_BAT_FAULT_MASK) {  		health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; @@ -1197,9 +1160,12 @@ static const struct power_supply_desc bq24190_battery_desc = {  static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)  {  	struct bq24190_dev_info *bdi = data; -	bool alert_userspace = false; +	const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK; +	const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK +				| BQ24190_REG_F_NTC_FAULT_MASK; +	bool alert_charger = false, alert_battery = false;  	u8 ss_reg = 0, f_reg = 0; -	int ret; +	int i, ret;  	pm_runtime_get_sync(bdi->dev); @@ -1209,6 +1175,32 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)  		goto out;  	} +	i = 0; +	do { +		ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); +		if (ret < 0) { +			dev_err(bdi->dev, "Can't read F reg: %d\n", ret); +			goto out; +		} +	} while (f_reg && ++i < 2); + +	if (f_reg != bdi->f_reg) { +		dev_info(bdi->dev, +			"Fault: boost %d, charge %d, battery %d, ntc %d\n", +			!!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK), +			!!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK), +			!!(f_reg & BQ24190_REG_F_BAT_FAULT_MASK), +			!!(f_reg & BQ24190_REG_F_NTC_FAULT_MASK)); + +		mutex_lock(&bdi->f_reg_lock); +		if ((bdi->f_reg & battery_mask_f) != (f_reg & battery_mask_f)) +			alert_battery = true; +		if ((bdi->f_reg & ~battery_mask_f) != (f_reg & ~battery_mask_f)) +			alert_charger = true; +		bdi->f_reg = f_reg; +		mutex_unlock(&bdi->f_reg_lock); +	} +  	if (ss_reg != bdi->ss_reg) {  		/*  		 * The device is in host mode so when PG_STAT goes from 1->0 @@ -1225,47 +1217,17 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)  					ret);  		} +		if ((bdi->ss_reg & battery_mask_ss) != (ss_reg & battery_mask_ss)) +			alert_battery = true; +		if ((bdi->ss_reg & ~battery_mask_ss) != (ss_reg & ~battery_mask_ss)) +			alert_charger = true;  		bdi->ss_reg = ss_reg; -		alert_userspace = true;  	} -	mutex_lock(&bdi->f_reg_lock); - -	ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); -	if (ret < 0) { -		mutex_unlock(&bdi->f_reg_lock); -		dev_err(bdi->dev, "Can't read F reg: %d\n", ret); -		goto out; -	} - -	if (f_reg != bdi->f_reg) { -		bdi->f_reg = f_reg; -		bdi->charger_health_valid = true; -		bdi->battery_health_valid = true; -		bdi->battery_status_valid = true; - -		alert_userspace = true; -	} - -	mutex_unlock(&bdi->f_reg_lock); - -	/* -	 * Sometimes bq24190 gives a steady trickle of interrupts even -	 * though the watchdog timer is turned off and neither the STATUS -	 * nor FAULT registers have changed.  Weed out these sprurious -	 * interrupts so userspace isn't alerted for no reason. -	 * In addition, the chip always generates an interrupt after -	 * register reset so we should ignore that one (the very first -	 * interrupt received). -	 */ -	if (alert_userspace) { -		if (!bdi->first_time) { -			power_supply_changed(bdi->charger); -			power_supply_changed(bdi->battery); -		} else { -			bdi->first_time = false; -		} -	} +	if (alert_charger) +		power_supply_changed(bdi->charger); +	if (alert_battery) +		power_supply_changed(bdi->battery);  out:  	pm_runtime_put_sync(bdi->dev); @@ -1300,6 +1262,10 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)  		goto out;  	ret = bq24190_set_mode_host(bdi); +	if (ret < 0) +		goto out; + +	ret = bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);  out:  	pm_runtime_put_sync(bdi->dev);  	return ret; @@ -1375,10 +1341,8 @@ static int bq24190_probe(struct i2c_client *client,  	bdi->model = id->driver_data;  	strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);  	mutex_init(&bdi->f_reg_lock); -	bdi->first_time = true; -	bdi->charger_health_valid = false; -	bdi->battery_health_valid = false; -	bdi->battery_status_valid = false; +	bdi->f_reg = 0; +	bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */  	i2c_set_clientdata(client, bdi); @@ -1392,22 +1356,13 @@ static int bq24190_probe(struct i2c_client *client,  		return -EINVAL;  	} -	ret = devm_request_threaded_irq(dev, bdi->irq, NULL, -			bq24190_irq_handler_thread, -			IRQF_TRIGGER_RISING | IRQF_ONESHOT, -			"bq24190-charger", bdi); -	if (ret < 0) { -		dev_err(dev, "Can't set up irq handler\n"); -		goto out1; -	} -  	pm_runtime_enable(dev);  	pm_runtime_resume(dev);  	ret = bq24190_hw_init(bdi);  	if (ret < 0) {  		dev_err(dev, "Hardware init failed\n"); -		goto out2; +		goto out1;  	}  	charger_cfg.drv_data = bdi; @@ -1418,7 +1373,7 @@ static int bq24190_probe(struct i2c_client *client,  	if (IS_ERR(bdi->charger)) {  		dev_err(dev, "Can't register charger\n");  		ret = PTR_ERR(bdi->charger); -		goto out2; +		goto out1;  	}  	battery_cfg.drv_data = bdi; @@ -1427,27 +1382,39 @@ static int bq24190_probe(struct i2c_client *client,  	if (IS_ERR(bdi->battery)) {  		dev_err(dev, "Can't register battery\n");  		ret = PTR_ERR(bdi->battery); -		goto out3; +		goto out2;  	}  	ret = bq24190_sysfs_create_group(bdi);  	if (ret) {  		dev_err(dev, "Can't create sysfs entries\n"); +		goto out3; +	} + +	ret = devm_request_threaded_irq(dev, bdi->irq, NULL, +			bq24190_irq_handler_thread, +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +			"bq24190-charger", bdi); +	if (ret < 0) { +		dev_err(dev, "Can't set up irq handler\n");  		goto out4;  	}  	return 0;  out4: -	power_supply_unregister(bdi->battery); +	bq24190_sysfs_remove_group(bdi); +  out3: -	power_supply_unregister(bdi->charger); +	power_supply_unregister(bdi->battery); +  out2: -	pm_runtime_disable(dev); +	power_supply_unregister(bdi->charger); +  out1: +	pm_runtime_disable(dev);  	if (bdi->gpio_int)  		gpio_free(bdi->gpio_int); -  	return ret;  } @@ -1488,12 +1455,13 @@ static int bq24190_pm_resume(struct device *dev)  	struct i2c_client *client = to_i2c_client(dev);  	struct bq24190_dev_info *bdi = i2c_get_clientdata(client); -	bdi->charger_health_valid = false; -	bdi->battery_health_valid = false; -	bdi->battery_status_valid = false; +	bdi->f_reg = 0; +	bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */  	pm_runtime_get_sync(bdi->dev);  	bq24190_register_reset(bdi); +	bq24190_set_mode_host(bdi); +	bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);  	pm_runtime_put_sync(bdi->dev);  	/* Things may have changed while suspended so alert upper layer */ diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index eb7783b42e0a..eb0145380def 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -50,6 +50,8 @@ struct bq24735 {  	struct bq24735_platform		*pdata;  	struct mutex			lock;  	struct gpio_desc		*status_gpio; +	struct delayed_work		poll; +	u32				poll_interval;  	bool				charging;  }; @@ -105,26 +107,6 @@ static int bq24735_update_word(struct i2c_client *client, u8 reg,  	return bq24735_write_word(client, reg, tmp);  } -static inline int bq24735_enable_charging(struct bq24735 *charger) -{ -	if (charger->pdata->ext_control) -		return 0; - -	return bq24735_update_word(charger->client, BQ24735_CHG_OPT, -				   BQ24735_CHG_OPT_CHARGE_DISABLE, -				   ~BQ24735_CHG_OPT_CHARGE_DISABLE); -} - -static inline int bq24735_disable_charging(struct bq24735 *charger) -{ -	if (charger->pdata->ext_control) -		return 0; - -	return bq24735_update_word(charger->client, BQ24735_CHG_OPT, -				   BQ24735_CHG_OPT_CHARGE_DISABLE, -				   BQ24735_CHG_OPT_CHARGE_DISABLE); -} -  static int bq24735_config_charger(struct bq24735 *charger)  {  	struct bq24735_platform *pdata = charger->pdata; @@ -176,6 +158,31 @@ static int bq24735_config_charger(struct bq24735 *charger)  	return 0;  } +static inline int bq24735_enable_charging(struct bq24735 *charger) +{ +	int ret; + +	if (charger->pdata->ext_control) +		return 0; + +	ret = bq24735_config_charger(charger); +	if (ret) +		return ret; + +	return bq24735_update_word(charger->client, BQ24735_CHG_OPT, +				   BQ24735_CHG_OPT_CHARGE_DISABLE, 0); +} + +static inline int bq24735_disable_charging(struct bq24735 *charger) +{ +	if (charger->pdata->ext_control) +		return 0; + +	return bq24735_update_word(charger->client, BQ24735_CHG_OPT, +				   BQ24735_CHG_OPT_CHARGE_DISABLE, +				   BQ24735_CHG_OPT_CHARGE_DISABLE); +} +  static bool bq24735_charger_is_present(struct bq24735 *charger)  {  	if (charger->status_gpio) { @@ -185,7 +192,7 @@ static bool bq24735_charger_is_present(struct bq24735 *charger)  		ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);  		if (ac < 0) { -			dev_err(&charger->client->dev, +			dev_dbg(&charger->client->dev,  				"Failed to read charger options : %d\n",  				ac);  			return false; @@ -210,11 +217,8 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)  	return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);  } -static irqreturn_t bq24735_charger_isr(int irq, void *devid) +static void bq24735_update(struct bq24735 *charger)  { -	struct power_supply *psy = devid; -	struct bq24735 *charger = to_bq24735(psy); -  	mutex_lock(&charger->lock);  	if (charger->charging && bq24735_charger_is_present(charger)) @@ -224,11 +228,29 @@ static irqreturn_t bq24735_charger_isr(int irq, void *devid)  	mutex_unlock(&charger->lock); -	power_supply_changed(psy); +	power_supply_changed(charger->charger); +} + +static irqreturn_t bq24735_charger_isr(int irq, void *devid) +{ +	struct power_supply *psy = devid; +	struct bq24735 *charger = to_bq24735(psy); + +	bq24735_update(charger);  	return IRQ_HANDLED;  } +static void bq24735_poll(struct work_struct *work) +{ +	struct bq24735 *charger = container_of(work, struct bq24735, poll.work); + +	bq24735_update(charger); + +	schedule_delayed_work(&charger->poll, +			      msecs_to_jiffies(charger->poll_interval)); +} +  static int bq24735_charger_get_property(struct power_supply *psy,  					enum power_supply_property psp,  					union power_supply_propval *val) @@ -276,7 +298,6 @@ static int bq24735_charger_set_property(struct power_supply *psy,  			mutex_unlock(&charger->lock);  			if (ret)  				return ret; -			bq24735_config_charger(charger);  			break;  		case POWER_SUPPLY_STATUS_DISCHARGING:  		case POWER_SUPPLY_STATUS_NOT_CHARGING: @@ -395,7 +416,7 @@ static int bq24735_charger_probe(struct i2c_client *client,  		return ret;  	} -	if (!charger->status_gpio || bq24735_charger_is_present(charger)) { +	if (bq24735_charger_is_present(charger)) {  		ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);  		if (ret < 0) {  			dev_err(&client->dev, "Failed to read manufacturer id : %d\n", @@ -416,16 +437,7 @@ static int bq24735_charger_probe(struct i2c_client *client,  				"device id mismatch. 0x000b != 0x%04x\n", ret);  			return -ENODEV;  		} -	} - -	ret = bq24735_config_charger(charger); -	if (ret < 0) { -		dev_err(&client->dev, "failed in configuring charger"); -		return ret; -	} -	/* check for AC adapter presence */ -	if (bq24735_charger_is_present(charger)) {  		ret = bq24735_enable_charging(charger);  		if (ret < 0) {  			dev_err(&client->dev, "Failed to enable charging\n"); @@ -456,11 +468,32 @@ static int bq24735_charger_probe(struct i2c_client *client,  				client->irq, ret);  			return ret;  		} +	} else { +		ret = device_property_read_u32(&client->dev, "poll-interval", +					       &charger->poll_interval); +		if (ret) +			return 0; +		if (!charger->poll_interval) +			return 0; + +		INIT_DELAYED_WORK(&charger->poll, bq24735_poll); +		schedule_delayed_work(&charger->poll, +				      msecs_to_jiffies(charger->poll_interval));  	}  	return 0;  } +static int bq24735_charger_remove(struct i2c_client *client) +{ +	struct bq24735 *charger = i2c_get_clientdata(client); + +	if (charger->poll_interval) +		cancel_delayed_work_sync(&charger->poll); + +	return 0; +} +  static const struct i2c_device_id bq24735_charger_id[] = {  	{ "bq24735-charger", 0 },  	{} @@ -479,6 +512,7 @@ static struct i2c_driver bq24735_charger_driver = {  		.of_match_table = bq24735_match_ids,  	},  	.probe = bq24735_charger_probe, +	.remove = bq24735_charger_remove,  	.id_table = bq24735_charger_id,  }; diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 08c36b8e04bd..398801a21b86 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -22,8 +22,14 @@   * http://www.ti.com/product/bq27010   * http://www.ti.com/product/bq27210   * http://www.ti.com/product/bq27500 + * http://www.ti.com/product/bq27510-g1 + * http://www.ti.com/product/bq27510-g2   * http://www.ti.com/product/bq27510-g3   * http://www.ti.com/product/bq27520-g4 + * http://www.ti.com/product/bq27520-g1 + * http://www.ti.com/product/bq27520-g2 + * http://www.ti.com/product/bq27520-g3 + * http://www.ti.com/product/bq27520-g4   * http://www.ti.com/product/bq27530-g1   * http://www.ti.com/product/bq27531-g1   * http://www.ti.com/product/bq27541-g1 @@ -145,7 +151,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {  		[BQ27XXX_REG_DCAP] = 0x76,  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,  	}, -	[BQ27500] = { +	[BQ2750X] = {  		[BQ27XXX_REG_CTRL] = 0x00,  		[BQ27XXX_REG_TEMP] = 0x06,  		[BQ27XXX_REG_INT_TEMP] = 0x28, @@ -164,7 +170,83 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {  		[BQ27XXX_REG_DCAP] = 0x3c,  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,  	}, -	[BQ27510] = { +	[BQ2751X] = { +		[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] = 0x1e, +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR, +		[BQ27XXX_REG_SOC] = 0x20, +		[BQ27XXX_REG_DCAP] = 0x2e, +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR, +	}, +	[BQ27500] = { +		[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] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27510G1] = { +		[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] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27510G2] = { +		[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] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27510G3] = {  		[BQ27XXX_REG_CTRL] = 0x00,  		[BQ27XXX_REG_TEMP] = 0x06,  		[BQ27XXX_REG_INT_TEMP] = 0x28, @@ -183,6 +265,82 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {  		[BQ27XXX_REG_DCAP] = 0x2e,  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,  	}, +	[BQ27520G1] = { +		[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] = INVALID_REG_ADDR, +		[BQ27XXX_REG_AE] = 0x22, +		[BQ27XXX_REG_SOC] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27520G2] = { +		[BQ27XXX_REG_CTRL] = 0x00, +		[BQ27XXX_REG_TEMP] = 0x06, +		[BQ27XXX_REG_INT_TEMP] = 0x36, +		[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] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27520G3] = { +		[BQ27XXX_REG_CTRL] = 0x00, +		[BQ27XXX_REG_TEMP] = 0x06, +		[BQ27XXX_REG_INT_TEMP] = 0x36, +		[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] = 0x1c, +		[BQ27XXX_REG_TTECP] = 0x26, +		[BQ27XXX_REG_NAC] = 0x0c, +		[BQ27XXX_REG_FCC] = 0x12, +		[BQ27XXX_REG_CYCT] = 0x2a, +		[BQ27XXX_REG_AE] = 0x22, +		[BQ27XXX_REG_SOC] = 0x2c, +		[BQ27XXX_REG_DCAP] = 0x3c, +		[BQ27XXX_REG_AP] = 0x24, +	}, +	[BQ27520G4] = { +		[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] = 0x1c, +		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, +		[BQ27XXX_REG_NAC] = 0x0c, +		[BQ27XXX_REG_FCC] = 0x12, +		[BQ27XXX_REG_CYCT] = 0x1e, +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR, +		[BQ27XXX_REG_SOC] = 0x20, +		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR, +	},  	[BQ27530] = {  		[BQ27XXX_REG_CTRL] = 0x00,  		[BQ27XXX_REG_TEMP] = 0x06, @@ -303,6 +461,42 @@ static enum power_supply_property bq27010_battery_props[] = {  	POWER_SUPPLY_PROP_MANUFACTURER,  }; +static enum power_supply_property bq2750x_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq2751x_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; +  static enum power_supply_property bq27500_battery_props[] = {  	POWER_SUPPLY_PROP_STATUS,  	POWER_SUPPLY_PROP_PRESENT, @@ -312,6 +506,69 @@ static enum power_supply_property bq27500_battery_props[] = {  	POWER_SUPPLY_PROP_CAPACITY_LEVEL,  	POWER_SUPPLY_PROP_TEMP,  	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27510g1_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27510g2_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27510g3_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,  	POWER_SUPPLY_PROP_TECHNOLOGY,  	POWER_SUPPLY_PROP_CHARGE_FULL,  	POWER_SUPPLY_PROP_CHARGE_NOW, @@ -321,7 +578,27 @@ static enum power_supply_property bq27500_battery_props[] = {  	POWER_SUPPLY_PROP_MANUFACTURER,  }; -static enum power_supply_property bq27510_battery_props[] = { +static enum power_supply_property bq27520g1_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27520g2_battery_props[] = {  	POWER_SUPPLY_PROP_STATUS,  	POWER_SUPPLY_PROP_PRESENT,  	POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -330,11 +607,51 @@ static enum power_supply_property bq27510_battery_props[] = {  	POWER_SUPPLY_PROP_CAPACITY_LEVEL,  	POWER_SUPPLY_PROP_TEMP,  	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,  	POWER_SUPPLY_PROP_TECHNOLOGY,  	POWER_SUPPLY_PROP_CHARGE_FULL,  	POWER_SUPPLY_PROP_CHARGE_NOW,  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,  	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27520g3_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property bq27520g4_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CYCLE_COUNT,  	POWER_SUPPLY_PROP_HEALTH,  	POWER_SUPPLY_PROP_MANUFACTURER,  }; @@ -421,8 +738,16 @@ static struct {  } bq27xxx_battery_props[] = {  	BQ27XXX_PROP(BQ27000, bq27000_battery_props),  	BQ27XXX_PROP(BQ27010, bq27010_battery_props), +	BQ27XXX_PROP(BQ2750X, bq2750x_battery_props), +	BQ27XXX_PROP(BQ2751X, bq2751x_battery_props),  	BQ27XXX_PROP(BQ27500, bq27500_battery_props), -	BQ27XXX_PROP(BQ27510, bq27510_battery_props), +	BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props), +	BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props), +	BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props), +	BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props), +	BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props), +	BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props), +	BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props),  	BQ27XXX_PROP(BQ27530, bq27530_battery_props),  	BQ27XXX_PROP(BQ27541, bq27541_battery_props),  	BQ27XXX_PROP(BQ27545, bq27545_battery_props), @@ -674,13 +999,26 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)   */  static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)  { -	if (di->chip == BQ27500 || di->chip == BQ27510 || -	    di->chip == BQ27541 || di->chip == BQ27545) +	switch (di->chip) { +	case BQ2750X: +	case BQ2751X: +	case BQ27500: +	case BQ27510G1: +	case BQ27510G2: +	case BQ27510G3: +	case BQ27520G1: +	case BQ27520G2: +	case BQ27520G3: +	case BQ27520G4: +	case BQ27541: +	case BQ27545:  		return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); -	if (di->chip == BQ27530 || di->chip == BQ27421) +	case BQ27530: +	case BQ27421:  		return flags & BQ27XXX_FLAG_OT; - -	return false; +	default: +		return false; +	}  }  /* diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 5c5c3a6f9923..c68fbc3fe50a 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -148,9 +148,17 @@ static int bq27xxx_battery_i2c_remove(struct i2c_client *client)  static const struct i2c_device_id bq27xxx_i2c_id_table[] = {  	{ "bq27200", BQ27000 },  	{ "bq27210", BQ27010 }, -	{ "bq27500", BQ27500 }, -	{ "bq27510", BQ27510 }, -	{ "bq27520", BQ27510 }, +	{ "bq27500", BQ2750X }, +	{ "bq27510", BQ2751X }, +	{ "bq27520", BQ2751X }, +	{ "bq27500-1", BQ27500 }, +	{ "bq27510g1", BQ27510G1 }, +	{ "bq27510g2", BQ27510G2 }, +	{ "bq27510g3", BQ27510G3 }, +	{ "bq27520g1", BQ27520G1 }, +	{ "bq27520g2", BQ27520G2 }, +	{ "bq27520g3", BQ27520G3 }, +	{ "bq27520g4", BQ27520G4 },  	{ "bq27530", BQ27530 },  	{ "bq27531", BQ27530 },  	{ "bq27541", BQ27541 }, @@ -173,6 +181,14 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {  	{ .compatible = "ti,bq27500" },  	{ .compatible = "ti,bq27510" },  	{ .compatible = "ti,bq27520" }, +	{ .compatible = "ti,bq27500-1" }, +	{ .compatible = "ti,bq27510g1" }, +	{ .compatible = "ti,bq27510g2" }, +	{ .compatible = "ti,bq27510g3" }, +	{ .compatible = "ti,bq27520g1" }, +	{ .compatible = "ti,bq27520g2" }, +	{ .compatible = "ti,bq27520g3" }, +	{ .compatible = "ti,bq27520g4" },  	{ .compatible = "ti,bq27530" },  	{ .compatible = "ti,bq27531" },  	{ .compatible = "ti,bq27541" }, diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index c5869b1941ac..001731e88718 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -14,7 +14,7 @@   */  #include <linux/device.h> -#include <linux/gpio.h> +#include <linux/gpio.h> /* For legacy platform data */  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/kernel.h> @@ -23,7 +23,7 @@  #include <linux/power_supply.h>  #include <linux/slab.h>  #include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h>  #include <linux/power/gpio-charger.h> @@ -34,6 +34,8 @@ struct gpio_charger {  	struct power_supply *charger;  	struct power_supply_desc charger_desc; +	struct gpio_desc *gpiod; +	bool legacy_gpio_requested;  };  static irqreturn_t gpio_charger_irq(int irq, void *devid) @@ -58,7 +60,8 @@ static int gpio_charger_get_property(struct power_supply *psy,  	switch (psp) {  	case POWER_SUPPLY_PROP_ONLINE: -		val->intval = !!gpio_get_value_cansleep(pdata->gpio); +		val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod); +		/* This xor is only ever used with legacy pdata GPIO */  		val->intval ^= pdata->gpio_active_low;  		break;  	default: @@ -78,7 +81,6 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)  	struct device_node *np = dev->of_node;  	struct gpio_charger_platform_data *pdata;  	const char *chargetype; -	enum of_gpio_flags flags;  	int ret;  	if (!np) @@ -89,16 +91,6 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)  		return ERR_PTR(-ENOMEM);  	pdata->name = np->name; - -	pdata->gpio = of_get_gpio_flags(np, 0, &flags); -	if (pdata->gpio < 0) { -		if (pdata->gpio != -EPROBE_DEFER) -			dev_err(dev, "could not get charger gpio\n"); -		return ERR_PTR(pdata->gpio); -	} - -	pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW); -  	pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;  	ret = of_property_read_string(np, "charger-type", &chargetype);  	if (ret >= 0) { @@ -144,11 +136,6 @@ static int gpio_charger_probe(struct platform_device *pdev)  		}  	} -	if (!gpio_is_valid(pdata->gpio)) { -		dev_err(&pdev->dev, "Invalid gpio pin\n"); -		return -EINVAL; -	} -  	gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),  					GFP_KERNEL);  	if (!gpio_charger) { @@ -156,6 +143,45 @@ static int gpio_charger_probe(struct platform_device *pdev)  		return -ENOMEM;  	} +	/* +	 * This will fetch a GPIO descriptor from device tree, ACPI or +	 * boardfile descriptor tables. It's good to try this first. +	 */ +	gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN); + +	/* +	 * If this fails and we're not using device tree, try the +	 * legacy platform data method. +	 */ +	if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) { +		/* Non-DT: use legacy GPIO numbers */ +		if (!gpio_is_valid(pdata->gpio)) { +			dev_err(&pdev->dev, "Invalid gpio pin in pdata\n"); +			return -EINVAL; +		} +		ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); +		if (ret) { +			dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", +				ret); +			return ret; +		} +		gpio_charger->legacy_gpio_requested = true; +		ret = gpio_direction_input(pdata->gpio); +		if (ret) { +			dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", +				ret); +			goto err_gpio_free; +		} +		/* Then convert this to gpiod for now */ +		gpio_charger->gpiod = gpio_to_desc(pdata->gpio); +	} else if (IS_ERR(gpio_charger->gpiod)) { +		/* Just try again if this happens */ +		if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER) +			return -EPROBE_DEFER; +		dev_err(&pdev->dev, "error getting GPIO descriptor\n"); +		return PTR_ERR(gpio_charger->gpiod); +	} +  	charger_desc = &gpio_charger->charger_desc;  	charger_desc->name = pdata->name ? pdata->name : "gpio-charger"; @@ -169,17 +195,6 @@ static int gpio_charger_probe(struct platform_device *pdev)  	psy_cfg.of_node = pdev->dev.of_node;  	psy_cfg.drv_data = gpio_charger; -	ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); -	if (ret) { -		dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); -		goto err_free; -	} -	ret = gpio_direction_input(pdata->gpio); -	if (ret) { -		dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); -		goto err_gpio_free; -	} -  	gpio_charger->pdata = pdata;  	gpio_charger->charger = power_supply_register(&pdev->dev, @@ -191,7 +206,7 @@ static int gpio_charger_probe(struct platform_device *pdev)  		goto err_gpio_free;  	} -	irq = gpio_to_irq(pdata->gpio); +	irq = gpiod_to_irq(gpio_charger->gpiod);  	if (irq > 0) {  		ret = request_any_context_irq(irq, gpio_charger_irq,  				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, @@ -209,8 +224,8 @@ static int gpio_charger_probe(struct platform_device *pdev)  	return 0;  err_gpio_free: -	gpio_free(pdata->gpio); -err_free: +	if (gpio_charger->legacy_gpio_requested) +		gpio_free(pdata->gpio);  	return ret;  } @@ -223,7 +238,8 @@ static int gpio_charger_remove(struct platform_device *pdev)  	power_supply_unregister(gpio_charger->charger); -	gpio_free(gpio_charger->pdata->gpio); +	if (gpio_charger->legacy_gpio_requested) +		gpio_free(gpio_charger->pdata->gpio);  	return 0;  } diff --git a/drivers/power/supply/intel_mid_battery.c b/drivers/power/supply/intel_mid_battery.c deleted file mode 100644 index dc7feef1bea4..000000000000 --- a/drivers/power/supply/intel_mid_battery.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - * intel_mid_battery.c - Intel MID PMIC Battery Driver - * - * Copyright (C) 2009 Intel Corporation - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Author: Nithish Mahalingam <nithish.mahalingam@intel.com> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/jiffies.h> -#include <linux/param.h> -#include <linux/device.h> -#include <linux/spi/spi.h> -#include <linux/platform_device.h> -#include <linux/power_supply.h> - -#include <asm/intel_scu_ipc.h> - -#define DRIVER_NAME "pmic_battery" - -/********************************************************************* - *		Generic defines - *********************************************************************/ - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages."); - -#define PMIC_BATT_DRV_INFO_UPDATED	1 -#define PMIC_BATT_PRESENT		1 -#define PMIC_BATT_NOT_PRESENT		0 -#define PMIC_USB_PRESENT		PMIC_BATT_PRESENT -#define PMIC_USB_NOT_PRESENT		PMIC_BATT_NOT_PRESENT - -/* pmic battery register related */ -#define PMIC_BATT_CHR_SCHRGINT_ADDR	0xD2 -#define PMIC_BATT_CHR_SBATOVP_MASK	(1 << 1) -#define PMIC_BATT_CHR_STEMP_MASK	(1 << 2) -#define PMIC_BATT_CHR_SCOMP_MASK	(1 << 3) -#define PMIC_BATT_CHR_SUSBDET_MASK	(1 << 4) -#define PMIC_BATT_CHR_SBATDET_MASK	(1 << 5) -#define PMIC_BATT_CHR_SDCLMT_MASK	(1 << 6) -#define PMIC_BATT_CHR_SUSBOVP_MASK	(1 << 7) -#define PMIC_BATT_CHR_EXCPT_MASK	0x86 - -#define PMIC_BATT_ADC_ACCCHRG_MASK	(1 << 31) -#define PMIC_BATT_ADC_ACCCHRGVAL_MASK	0x7FFFFFFF - -/* pmic ipc related */ -#define PMIC_BATT_CHR_IPC_FCHRG_SUBID	0x4 -#define PMIC_BATT_CHR_IPC_TCHRG_SUBID	0x6 - -/* types of battery charging */ -enum batt_charge_type { -	BATT_USBOTG_500MA_CHARGE, -	BATT_USBOTG_TRICKLE_CHARGE, -}; - -/* valid battery events */ -enum batt_event { -	BATT_EVENT_BATOVP_EXCPT, -	BATT_EVENT_USBOVP_EXCPT, -	BATT_EVENT_TEMP_EXCPT, -	BATT_EVENT_DCLMT_EXCPT, -	BATT_EVENT_EXCPT -}; - - -/********************************************************************* - *		Battery properties - *********************************************************************/ - -/* - * pmic battery info - */ -struct pmic_power_module_info { -	bool is_dev_info_updated; -	struct device *dev; -	/* pmic battery data */ -	unsigned long update_time;		/* jiffies when data read */ -	unsigned int usb_is_present; -	unsigned int batt_is_present; -	unsigned int batt_health; -	unsigned int usb_health; -	unsigned int batt_status; -	unsigned int batt_charge_now;		/* in mAS */ -	unsigned int batt_prev_charge_full;	/* in mAS */ -	unsigned int batt_charge_rate;		/* in units per second */ - -	struct power_supply *usb; -	struct power_supply *batt; -	int irq;				/* GPE_ID or IRQ# */ -	struct workqueue_struct *monitor_wqueue; -	struct delayed_work monitor_battery; -	struct work_struct handler; -}; - -static unsigned int delay_time = 2000;	/* in ms */ - -/* - * pmic ac properties - */ -static enum power_supply_property pmic_usb_props[] = { -	POWER_SUPPLY_PROP_PRESENT, -	POWER_SUPPLY_PROP_HEALTH, -}; - -/* - * pmic battery properties - */ -static enum power_supply_property pmic_battery_props[] = { -	POWER_SUPPLY_PROP_STATUS, -	POWER_SUPPLY_PROP_HEALTH, -	POWER_SUPPLY_PROP_PRESENT, -	POWER_SUPPLY_PROP_CHARGE_NOW, -	POWER_SUPPLY_PROP_CHARGE_FULL, -}; - - -/* - * Glue functions for talking to the IPC - */ - -struct battery_property { -	u32 capacity;	/* Charger capacity */ -	u8  crnt;	/* Quick charge current value*/ -	u8  volt;	/* Fine adjustment of constant charge voltage */ -	u8  prot;	/* CHRGPROT register value */ -	u8  prot2;	/* CHRGPROT1 register value */ -	u8  timer;	/* Charging timer */ -}; - -#define IPCMSG_BATTERY		0xEF - -/* Battery coulomb counter accumulator commands */ -#define IPC_CMD_CC_WR		  0 /* Update coulomb counter value */ -#define IPC_CMD_CC_RD		  1 /* Read coulomb counter value */ -#define IPC_CMD_BATTERY_PROPERTY  2 /* Read Battery property */ - -/** - *	pmic_scu_ipc_battery_cc_read	-	read battery cc - *	@value: battery coulomb counter read - * - *	Reads the battery couloumb counter value, returns 0 on success, or - *	an error code - * - *	This function may sleep. Locking for SCU accesses is handled for - *	the caller. - */ -static int pmic_scu_ipc_battery_cc_read(u32 *value) -{ -	return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD, -					NULL, 0, value, 1); -} - -/** - *	pmic_scu_ipc_battery_property_get	-	fetch properties - *	@prop: battery properties - * - *	Retrieve the battery properties from the power management - * - *	This function may sleep. Locking for SCU accesses is handled for - *	the caller. - */ -static int pmic_scu_ipc_battery_property_get(struct battery_property *prop) -{ -	u32 data[3]; -	u8 *p = (u8 *)&data[1]; -	int err = intel_scu_ipc_command(IPCMSG_BATTERY, -				IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3); - -	prop->capacity = data[0]; -	prop->crnt = *p++; -	prop->volt = *p++; -	prop->prot = *p++; -	prop->prot2 = *p++; -	prop->timer = *p++; - -	return err; -} - -/** - *	pmic_scu_ipc_set_charger	-	set charger - *	@charger: charger to select - * - *	Switch the charging mode for the SCU - */ - -static int pmic_scu_ipc_set_charger(int charger) -{ -	return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger); -} - -/** - * pmic_battery_log_event - log battery events - * @event: battery event to be logged - * Context: can sleep - * - * There are multiple battery events which may be of interest to users; - * this battery function logs the different battery events onto the - * kernel log messages. - */ -static void pmic_battery_log_event(enum batt_event event) -{ -	printk(KERN_WARNING "pmic-battery: "); -	switch (event) { -	case BATT_EVENT_BATOVP_EXCPT: -		printk(KERN_CONT "battery overvoltage condition\n"); -		break; -	case BATT_EVENT_USBOVP_EXCPT: -		printk(KERN_CONT "usb charger overvoltage condition\n"); -		break; -	case BATT_EVENT_TEMP_EXCPT: -		printk(KERN_CONT "high battery temperature condition\n"); -		break; -	case BATT_EVENT_DCLMT_EXCPT: -		printk(KERN_CONT "over battery charge current condition\n"); -		break; -	default: -		printk(KERN_CONT "charger/battery exception %d\n", event); -		break; -	} -} - -/** - * pmic_battery_read_status - read battery status information - * @pbi: device info structure to update the read information - * Context: can sleep - * - * PMIC power source information need to be updated based on the data read - * from the PMIC battery registers. - * - */ -static void pmic_battery_read_status(struct pmic_power_module_info *pbi) -{ -	unsigned int update_time_intrvl; -	unsigned int chrg_val; -	u32 ccval; -	u8 r8; -	struct battery_property batt_prop; -	int batt_present = 0; -	int usb_present = 0; -	int batt_exception = 0; - -	/* make sure the last batt_status read happened delay_time before */ -	if (pbi->update_time && time_before(jiffies, pbi->update_time + -						msecs_to_jiffies(delay_time))) -		return; - -	update_time_intrvl = jiffies_to_msecs(jiffies -	pbi->update_time); -	pbi->update_time = jiffies; - -	/* read coulomb counter registers and schrgint register */ -	if (pmic_scu_ipc_battery_cc_read(&ccval)) { -		dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", -								__func__); -		return; -	} - -	if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { -		dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", -								__func__); -		return; -	} - -	/* -	 * set pmic_power_module_info members based on pmic register values -	 * read. -	 */ - -	/* set batt_is_present */ -	if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { -		pbi->batt_is_present = PMIC_BATT_PRESENT; -		batt_present = 1; -	} else { -		pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; -		pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; -		pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; -	} - -	/* set batt_health */ -	if (batt_present) { -		if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) { -			pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; -			pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; -			pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT); -			batt_exception = 1; -		} else if (r8 & PMIC_BATT_CHR_STEMP_MASK) { -			pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; -			pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; -			pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT); -			batt_exception = 1; -		} else { -			pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; -			if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) { -				/* PMIC will change charging current automatically */ -				pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); -			} -		} -	} - -	/* set usb_is_present */ -	if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { -		pbi->usb_is_present = PMIC_USB_PRESENT; -		usb_present = 1; -	} else { -		pbi->usb_is_present = PMIC_USB_NOT_PRESENT; -		pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; -	} - -	if (usb_present) { -		if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) { -			pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; -			pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT); -		} else { -			pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; -		} -	} - -	chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK; - -	/* set batt_prev_charge_full to battery capacity the first time */ -	if (!pbi->is_dev_info_updated) { -		if (pmic_scu_ipc_battery_property_get(&batt_prop)) { -			dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", -								__func__); -			return; -		} -		pbi->batt_prev_charge_full = batt_prop.capacity; -	} - -	/* set batt_status */ -	if (batt_present && !batt_exception) { -		if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { -			pbi->batt_status = POWER_SUPPLY_STATUS_FULL; -			pbi->batt_prev_charge_full = chrg_val; -		} else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) { -			pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING; -		} else { -			pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING; -		} -	} - -	/* set batt_charge_rate */ -	if (pbi->is_dev_info_updated && batt_present && !batt_exception) { -		if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) { -			if (pbi->batt_charge_now - chrg_val) { -				pbi->batt_charge_rate = ((pbi->batt_charge_now - -					chrg_val) * 1000 * 60) / -					update_time_intrvl; -			} -		} else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) { -			if (chrg_val - pbi->batt_charge_now) { -				pbi->batt_charge_rate = ((chrg_val - -					pbi->batt_charge_now) * 1000 * 60) / -					update_time_intrvl; -			} -		} else -			pbi->batt_charge_rate = 0; -	} else { -		pbi->batt_charge_rate = -1; -	} - -	/* batt_charge_now */ -	if (batt_present && !batt_exception) -		pbi->batt_charge_now = chrg_val; -	else -		pbi->batt_charge_now = -1; - -	pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED; -} - -/** - * pmic_usb_get_property - usb power source get property - * @psy: usb power supply context - * @psp: usb power source property - * @val: usb power source property value - * Context: can sleep - * - * PMIC usb power source property needs to be provided to power_supply - * subsytem for it to provide the information to users. - */ -static int pmic_usb_get_property(struct power_supply *psy, -				enum power_supply_property psp, -				union power_supply_propval *val) -{ -	struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy); - -	/* update pmic_power_module_info members */ -	pmic_battery_read_status(pbi); - -	switch (psp) { -	case POWER_SUPPLY_PROP_PRESENT: -		val->intval = pbi->usb_is_present; -		break; -	case POWER_SUPPLY_PROP_HEALTH: -		val->intval = pbi->usb_health; -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -static inline unsigned long mAStouAh(unsigned long v) -{ -	/* seconds to hours, mA to µA */ -	return (v * 1000) / 3600; -} - -/** - * pmic_battery_get_property - battery power source get property - * @psy: battery power supply context - * @psp: battery power source property - * @val: battery power source property value - * Context: can sleep - * - * PMIC battery power source property needs to be provided to power_supply - * subsytem for it to provide the information to users. - */ -static int pmic_battery_get_property(struct power_supply *psy, -				enum power_supply_property psp, -				union power_supply_propval *val) -{ -	struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy); - -	/* update pmic_power_module_info members */ -	pmic_battery_read_status(pbi); - -	switch (psp) { -	case POWER_SUPPLY_PROP_STATUS: -		val->intval = pbi->batt_status; -		break; -	case POWER_SUPPLY_PROP_HEALTH: -		val->intval = pbi->batt_health; -		break; -	case POWER_SUPPLY_PROP_PRESENT: -		val->intval = pbi->batt_is_present; -		break; -	case POWER_SUPPLY_PROP_CHARGE_NOW: -		val->intval = mAStouAh(pbi->batt_charge_now); -		break; -	case POWER_SUPPLY_PROP_CHARGE_FULL: -		val->intval = mAStouAh(pbi->batt_prev_charge_full); -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -/** - * pmic_battery_monitor - monitor battery status - * @work: work structure - * Context: can sleep - * - * PMIC battery status needs to be monitored for any change - * and information needs to be frequently updated. - */ -static void pmic_battery_monitor(struct work_struct *work) -{ -	struct pmic_power_module_info *pbi = container_of(work, -			struct pmic_power_module_info, monitor_battery.work); - -	/* update pmic_power_module_info members */ -	pmic_battery_read_status(pbi); -	queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10); -} - -/** - * pmic_battery_set_charger - set battery charger - * @pbi: device info structure - * @chrg: charge mode to set battery charger in - * Context: can sleep - * - * PMIC battery charger needs to be enabled based on the usb charge - * capabilities connected to the platform. - */ -static int pmic_battery_set_charger(struct pmic_power_module_info *pbi, -						enum batt_charge_type chrg) -{ -	int retval; - -	/* set usblmt bits and chrgcntl register bits appropriately */ -	switch (chrg) { -	case BATT_USBOTG_500MA_CHARGE: -		retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID); -		break; -	case BATT_USBOTG_TRICKLE_CHARGE: -		retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID); -		break; -	default: -		dev_warn(pbi->dev, "%s(): out of range usb charger " -						"charge detected\n", __func__); -		return -EINVAL; -	} - -	if (retval) { -		dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", -								__func__); -		return retval; -	} - -	return 0; -} - -/** - * pmic_battery_interrupt_handler - pmic battery interrupt handler - * Context: interrupt context - * - * PMIC battery interrupt handler which will be called with either - * battery full condition occurs or usb otg & battery connect - * condition occurs. - */ -static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev) -{ -	struct pmic_power_module_info *pbi = dev; - -	schedule_work(&pbi->handler); - -	return IRQ_HANDLED; -} - -/** - * pmic_battery_handle_intrpt - pmic battery service interrupt - * @work: work structure - * Context: can sleep - * - * PMIC battery needs to either update the battery status as full - * if it detects battery full condition caused the interrupt or needs - * to enable battery charger if it detects usb and battery detect - * caused the source of interrupt. - */ -static void pmic_battery_handle_intrpt(struct work_struct *work) -{ -	struct pmic_power_module_info *pbi = container_of(work, -				struct pmic_power_module_info, handler); -	enum batt_charge_type chrg; -	u8 r8; - -	if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { -		dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", -								__func__); -		return; -	} -	/* find the cause of the interrupt */ -	if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { -		pbi->batt_is_present = PMIC_BATT_PRESENT; -	} else { -		pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; -		pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; -		pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; -		return; -	} - -	if (r8 & PMIC_BATT_CHR_EXCPT_MASK) { -		pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; -		pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; -		pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; -		pmic_battery_log_event(BATT_EVENT_EXCPT); -		return; -	} else { -		pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; -		pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; -	} - -	if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { -		u32 ccval; -		pbi->batt_status = POWER_SUPPLY_STATUS_FULL; - -		if (pmic_scu_ipc_battery_cc_read(&ccval)) { -			dev_warn(pbi->dev, "%s(): ipc config cmd " -							"failed\n", __func__); -			return; -		} -		pbi->batt_prev_charge_full = ccval & -						PMIC_BATT_ADC_ACCCHRGVAL_MASK; -		return; -	} - -	if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { -		pbi->usb_is_present = PMIC_USB_PRESENT; -	} else { -		pbi->usb_is_present = PMIC_USB_NOT_PRESENT; -		pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; -		return; -	} - -	/* setup battery charging */ - -#if 0 -	/* check usb otg power capability and set charger accordingly */ -	retval = langwell_udc_maxpower(&power); -	if (retval) { -		dev_warn(pbi->dev, -		    "%s(): usb otg power query failed with error code %d\n", -			__func__, retval); -		return; -	} - -	if (power >= 500) -		chrg = BATT_USBOTG_500MA_CHARGE; -	else -#endif -		chrg = BATT_USBOTG_TRICKLE_CHARGE; - -	/* enable battery charging */ -	if (pmic_battery_set_charger(pbi, chrg)) { -		dev_warn(pbi->dev, -			"%s(): failed to set up battery charging\n", __func__); -		return; -	} - -	dev_dbg(pbi->dev, -		"pmic-battery: %s() - setting up battery charger successful\n", -			__func__); -} - -/* - * Description of power supplies - */ -static const struct power_supply_desc pmic_usb_desc = { -	.name		= "pmic-usb", -	.type		= POWER_SUPPLY_TYPE_USB, -	.properties	= pmic_usb_props, -	.num_properties	= ARRAY_SIZE(pmic_usb_props), -	.get_property	= pmic_usb_get_property, -}; - -static const struct power_supply_desc pmic_batt_desc = { -	.name		= "pmic-batt", -	.type		= POWER_SUPPLY_TYPE_BATTERY, -	.properties	= pmic_battery_props, -	.num_properties	= ARRAY_SIZE(pmic_battery_props), -	.get_property	= pmic_battery_get_property, -}; - -/** - * pmic_battery_probe - pmic battery initialize - * @irq: pmic battery device irq - * @dev: pmic battery device structure - * Context: can sleep - * - * PMIC battery initializes its internal data structue and other - * infrastructure components for it to work as expected. - */ -static int probe(int irq, struct device *dev) -{ -	int retval = 0; -	struct pmic_power_module_info *pbi; -	struct power_supply_config psy_cfg = {}; - -	dev_dbg(dev, "pmic-battery: found pmic battery device\n"); - -	pbi = kzalloc(sizeof(*pbi), GFP_KERNEL); -	if (!pbi) { -		dev_err(dev, "%s(): memory allocation failed\n", -								__func__); -		return -ENOMEM; -	} - -	pbi->dev = dev; -	pbi->irq = irq; -	dev_set_drvdata(dev, pbi); -	psy_cfg.drv_data = pbi; - -	/* initialize all required framework before enabling interrupts */ -	INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt); -	INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor); -	pbi->monitor_wqueue = alloc_workqueue(dev_name(dev), WQ_MEM_RECLAIM, 0); -	if (!pbi->monitor_wqueue) { -		dev_err(dev, "%s(): wqueue init failed\n", __func__); -		retval = -ESRCH; -		goto wqueue_failed; -	} - -	/* register interrupt */ -	retval = request_irq(pbi->irq, pmic_battery_interrupt_handler, -							0, DRIVER_NAME, pbi); -	if (retval) { -		dev_err(dev, "%s(): cannot get IRQ\n", __func__); -		goto requestirq_failed; -	} - -	/* register pmic-batt with power supply subsystem */ -	pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg); -	if (IS_ERR(pbi->batt)) { -		dev_err(dev, -			"%s(): failed to register pmic battery device with power supply subsystem\n", -				__func__); -		retval = PTR_ERR(pbi->batt); -		goto power_reg_failed; -	} - -	dev_dbg(dev, "pmic-battery: %s() - pmic battery device " -		"registration with power supply subsystem successful\n", -		__func__); - -	queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1); - -	/* register pmic-usb with power supply subsystem */ -	pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg); -	if (IS_ERR(pbi->usb)) { -		dev_err(dev, -			"%s(): failed to register pmic usb device with power supply subsystem\n", -				__func__); -		retval = PTR_ERR(pbi->usb); -		goto power_reg_failed_1; -	} - -	if (debug) -		printk(KERN_INFO "pmic-battery: %s() - pmic usb device " -			"registration with power supply subsystem successful\n", -			__func__); - -	return retval; - -power_reg_failed_1: -	power_supply_unregister(pbi->batt); -power_reg_failed: -	cancel_delayed_work_sync(&pbi->monitor_battery); -requestirq_failed: -	destroy_workqueue(pbi->monitor_wqueue); -wqueue_failed: -	kfree(pbi); - -	return retval; -} - -static int platform_pmic_battery_probe(struct platform_device *pdev) -{ -	return probe(pdev->id, &pdev->dev); -} - -/** - * pmic_battery_remove - pmic battery finalize - * @dev: pmic battery device structure - * Context: can sleep - * - * PMIC battery finalizes its internal data structue and other - * infrastructure components that it initialized in - * pmic_battery_probe. - */ - -static int platform_pmic_battery_remove(struct platform_device *pdev) -{ -	struct pmic_power_module_info *pbi = platform_get_drvdata(pdev); - -	free_irq(pbi->irq, pbi); -	cancel_delayed_work_sync(&pbi->monitor_battery); -	destroy_workqueue(pbi->monitor_wqueue); - -	power_supply_unregister(pbi->usb); -	power_supply_unregister(pbi->batt); - -	cancel_work_sync(&pbi->handler); -	kfree(pbi); -	return 0; -} - -static struct platform_driver platform_pmic_battery_driver = { -	.driver = { -		.name = DRIVER_NAME, -	}, -	.probe = platform_pmic_battery_probe, -	.remove = platform_pmic_battery_remove, -}; - -module_platform_driver(platform_pmic_battery_driver); - -MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>"); -MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c new file mode 100644 index 000000000000..b91b1d2999dc --- /dev/null +++ b/drivers/power/supply/max14656_charger_detector.c @@ -0,0 +1,327 @@ +/* + * Maxim MAX14656 / AL32 USB Charger Detector driver + * + * Copyright (C) 2014 LG Electronics, Inc + * Copyright (C) 2016 Alexander Kurz <akurz@blala.de> + * + * Components from Maxim AL32 Charger detection Driver for MX50 Yoshi Board + * Copyright (C) Amazon Technologies Inc. All rights reserved. + * Manish Lachwani (lachwani@lab126.com) + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#include <linux/workqueue.h> +#include <linux/power_supply.h> + +#define MAX14656_MANUFACTURER	"Maxim Integrated" +#define MAX14656_NAME		"max14656" + +#define MAX14656_DEVICE_ID	0x00 +#define MAX14656_INTERRUPT_1	0x01 +#define MAX14656_INTERRUPT_2	0x02 +#define MAX14656_STATUS_1	0x03 +#define MAX14656_STATUS_2	0x04 +#define MAX14656_INTMASK_1	0x05 +#define MAX14656_INTMASK_2	0x06 +#define MAX14656_CONTROL_1	0x07 +#define MAX14656_CONTROL_2	0x08 +#define MAX14656_CONTROL_3	0x09 + +#define DEVICE_VENDOR_MASK	0xf0 +#define DEVICE_REV_MASK		0x0f +#define INT_EN_REG_MASK		BIT(4) +#define CHG_TYPE_INT_MASK	BIT(0) +#define STATUS1_VB_VALID_MASK	BIT(4) +#define STATUS1_CHG_TYPE_MASK	0xf +#define INT1_DCD_TIMEOUT_MASK	BIT(7) +#define CONTROL1_DEFAULT	0x0d +#define CONTROL1_INT_EN		BIT(4) +#define CONTROL1_INT_ACTIVE_HIGH	BIT(5) +#define CONTROL1_EDGE		BIT(7) +#define CONTROL2_DEFAULT	0x8e +#define CONTROL2_ADC_EN		BIT(0) +#define CONTROL3_DEFAULT	0x8d + +enum max14656_chg_type { +	MAX14656_NO_CHARGER	= 0, +	MAX14656_SDP_CHARGER, +	MAX14656_CDP_CHARGER, +	MAX14656_DCP_CHARGER, +	MAX14656_APPLE_500MA_CHARGER, +	MAX14656_APPLE_1A_CHARGER, +	MAX14656_APPLE_2A_CHARGER, +	MAX14656_SPECIAL_500MA_CHARGER, +	MAX14656_APPLE_12W, +	MAX14656_CHARGER_LAST +}; + +static const struct max14656_chg_type_props { +	enum power_supply_type type; +} chg_type_props[] = { +	{ POWER_SUPPLY_TYPE_UNKNOWN }, +	{ POWER_SUPPLY_TYPE_USB }, +	{ POWER_SUPPLY_TYPE_USB_CDP }, +	{ POWER_SUPPLY_TYPE_USB_DCP }, +	{ POWER_SUPPLY_TYPE_USB_DCP }, +	{ POWER_SUPPLY_TYPE_USB_DCP }, +	{ POWER_SUPPLY_TYPE_USB_DCP }, +	{ POWER_SUPPLY_TYPE_USB_DCP }, +	{ POWER_SUPPLY_TYPE_USB }, +}; + +struct max14656_chip { +	struct i2c_client	*client; +	struct power_supply	*detect_psy; +	struct power_supply_desc psy_desc; +	struct delayed_work	irq_work; + +	int irq; +	int online; +}; + +static int max14656_read_reg(struct i2c_client *client, int reg, u8 *val) +{ +	s32 ret; + +	ret = i2c_smbus_read_byte_data(client, reg); +	if (ret < 0) { +		dev_err(&client->dev, +			"i2c read fail: can't read from %02x: %d\n", +			reg, ret); +		return ret; +	} +	*val = ret; +	return 0; +} + +static int max14656_write_reg(struct i2c_client *client, int reg, u8 val) +{ +	s32 ret; + +	ret = i2c_smbus_write_byte_data(client, reg, val); +	if (ret < 0) { +		dev_err(&client->dev, +			"i2c write fail: can't write %02x to %02x: %d\n", +			val, reg, ret); +		return ret; +	} +	return 0; +} + +static int max14656_read_block_reg(struct i2c_client *client, u8 reg, +				  u8 length, u8 *val) +{ +	int ret; + +	ret = i2c_smbus_read_i2c_block_data(client, reg, length, val); +	if (ret < 0) { +		dev_err(&client->dev, "failed to block read reg 0x%x: %d\n", +				reg, ret); +		return ret; +	} + +	return 0; +} + +#define        REG_TOTAL_NUM   5 +static void max14656_irq_worker(struct work_struct *work) +{ +	struct max14656_chip *chip = +		container_of(work, struct max14656_chip, irq_work.work); + +	u8 buf[REG_TOTAL_NUM]; +	u8 chg_type; +	int ret = 0; + +	ret = max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID, +				      REG_TOTAL_NUM, buf); + +	if ((buf[MAX14656_STATUS_1] & STATUS1_VB_VALID_MASK) && +		(buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK)) { +		chg_type = buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK; +		if (chg_type < MAX14656_CHARGER_LAST) +			chip->psy_desc.type = chg_type_props[chg_type].type; +		else +			chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; +		chip->online = 1; +	} else { +		chip->online = 0; +		chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; +	} + +	power_supply_changed(chip->detect_psy); +} + +static irqreturn_t max14656_irq(int irq, void *dev_id) +{ +	struct max14656_chip *chip = dev_id; + +	schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(100)); + +	return IRQ_HANDLED; +} + +static int max14656_hw_init(struct max14656_chip *chip) +{ +	uint8_t val = 0; +	uint8_t rev; +	struct i2c_client *client = chip->client; + +	if (max14656_read_reg(client, MAX14656_DEVICE_ID, &val)) +		return -ENODEV; + +	if ((val & DEVICE_VENDOR_MASK) != 0x20) { +		dev_err(&client->dev, "wrong vendor ID %d\n", +			((val & DEVICE_VENDOR_MASK) >> 4)); +		return -ENODEV; +	} +	rev = val & DEVICE_REV_MASK; + +	/* Turn on ADC_EN */ +	if (max14656_write_reg(client, MAX14656_CONTROL_2, CONTROL2_ADC_EN)) +		return -EINVAL; + +	/* turn on interrupts and low power mode */ +	if (max14656_write_reg(client, MAX14656_CONTROL_1, +		CONTROL1_DEFAULT | +		CONTROL1_INT_EN | +		CONTROL1_INT_ACTIVE_HIGH | +		CONTROL1_EDGE)) +		return -EINVAL; + +	if (max14656_write_reg(client, MAX14656_INTMASK_1, 0x3)) +		return -EINVAL; + +	if (max14656_write_reg(client, MAX14656_INTMASK_2, 0x1)) +		return -EINVAL; + +	dev_info(&client->dev, "detected revision %d\n", rev); +	return 0; +} + +static int max14656_get_property(struct power_supply *psy, +			    enum power_supply_property psp, +			    union power_supply_propval *val) +{ +	struct max14656_chip *chip = power_supply_get_drvdata(psy); + +	switch (psp) { +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = chip->online; +		break; +	case POWER_SUPPLY_PROP_MODEL_NAME: +		val->strval = MAX14656_NAME; +		break; +	case POWER_SUPPLY_PROP_MANUFACTURER: +		val->strval = MAX14656_MANUFACTURER; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static enum power_supply_property max14656_battery_props[] = { +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_MODEL_NAME, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static int max14656_probe(struct i2c_client *client, +			  const struct i2c_device_id *id) +{ +	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); +	struct device *dev = &client->dev; +	struct power_supply_config psy_cfg = {}; +	struct max14656_chip *chip; +	int irq = client->irq; +	int ret = 0; + +	if (irq <= 0) { +		dev_err(dev, "invalid irq number: %d\n", irq); +		return -ENODEV; +	} + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { +		dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); +		return -ENODEV; +	} + +	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	psy_cfg.drv_data = chip; +	chip->client = client; +	chip->online = 0; +	chip->psy_desc.name = MAX14656_NAME; +	chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; +	chip->psy_desc.properties = max14656_battery_props; +	chip->psy_desc.num_properties = ARRAY_SIZE(max14656_battery_props); +	chip->psy_desc.get_property = max14656_get_property; +	chip->irq = irq; + +	ret = max14656_hw_init(chip); +	if (ret) +		return -ENODEV; + +	INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker); + +	ret = devm_request_irq(dev, chip->irq, max14656_irq, +			       IRQF_TRIGGER_FALLING, +			       MAX14656_NAME, chip); +	if (ret) { +		dev_err(dev, "request_irq %d failed\n", chip->irq); +		return -EINVAL; +	} +	enable_irq_wake(chip->irq); + +	chip->detect_psy = devm_power_supply_register(dev, +		       &chip->psy_desc, &psy_cfg); +	if (IS_ERR(chip->detect_psy)) { +		dev_err(dev, "power_supply_register failed\n"); +		return -EINVAL; +	} + +	schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(2000)); + +	return 0; +} + +static const struct i2c_device_id max14656_id[] = { +	{ "max14656", 0 }, +	{} +}; +MODULE_DEVICE_TABLE(i2c, max14656_id); + +static const struct of_device_id max14656_match_table[] = { +	{ .compatible = "maxim,max14656", }, +	{} +}; +MODULE_DEVICE_TABLE(of, max14656_match_table); + +static struct i2c_driver max14656_i2c_driver = { +	.driver = { +		.name	= "max14656", +		.of_match_table = max14656_match_table, +	}, +	.probe		= max14656_probe, +	.id_table	= max14656_id, +}; +module_i2c_driver(max14656_i2c_driver); + +MODULE_DESCRIPTION("MAX14656 USB charger detector"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/max8997_charger.c b/drivers/power/supply/max8997_charger.c index 290ddc12b040..fa861003fece 100644 --- a/drivers/power/supply/max8997_charger.c +++ b/drivers/power/supply/max8997_charger.c @@ -148,10 +148,8 @@ static int max8997_battery_probe(struct platform_device *pdev)  	charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),  				GFP_KERNEL); -	if (charger == NULL) { -		dev_err(&pdev->dev, "Cannot allocate memory.\n"); +	if (!charger)  		return -ENOMEM; -	}  	platform_set_drvdata(pdev, charger); @@ -161,7 +159,7 @@ static int max8997_battery_probe(struct platform_device *pdev)  	psy_cfg.drv_data = charger; -	charger->battery = power_supply_register(&pdev->dev, +	charger->battery = devm_power_supply_register(&pdev->dev,  						 &max8997_battery_desc,  						 &psy_cfg);  	if (IS_ERR(charger->battery)) { @@ -172,14 +170,6 @@ static int max8997_battery_probe(struct platform_device *pdev)  	return 0;  } -static int max8997_battery_remove(struct platform_device *pdev) -{ -	struct charger_data *charger = platform_get_drvdata(pdev); - -	power_supply_unregister(charger->battery); -	return 0; -} -  static const struct platform_device_id max8997_battery_id[] = {  	{ "max8997-battery", 0 },  	{ } @@ -191,7 +181,6 @@ static struct platform_driver max8997_battery_driver = {  		.name = "max8997-battery",  	},  	.probe = max8997_battery_probe, -	.remove = max8997_battery_remove,  	.id_table = max8997_battery_id,  }; diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index d05597b4e40f..b3c1873ad84d 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -393,7 +393,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)  {  	struct power_supply_config psy_cfg = {};  	struct pcf50633_mbc *mbc; -	int ret;  	int i;  	u8 mbcs1; @@ -419,8 +418,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)  					     &psy_cfg);  	if (IS_ERR(mbc->adapter)) {  		dev_err(mbc->pcf->dev, "failed to register adapter\n"); -		ret = PTR_ERR(mbc->adapter); -		return ret; +		return PTR_ERR(mbc->adapter);  	}  	mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc, @@ -428,8 +426,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)  	if (IS_ERR(mbc->usb)) {  		dev_err(mbc->pcf->dev, "failed to register usb\n");  		power_supply_unregister(mbc->adapter); -		ret = PTR_ERR(mbc->usb); -		return ret; +		return PTR_ERR(mbc->usb);  	}  	mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc, @@ -438,12 +435,10 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)  		dev_err(mbc->pcf->dev, "failed to register ac\n");  		power_supply_unregister(mbc->adapter);  		power_supply_unregister(mbc->usb); -		ret = PTR_ERR(mbc->ac); -		return ret; +		return PTR_ERR(mbc->ac);  	} -	ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); -	if (ret) +	if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))  		dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");  	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1); diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index b5896ba2a602..f6a0d245731d 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -35,6 +35,7 @@  #include <linux/regmap.h>  #include <linux/slab.h>  #include <linux/extcon.h> +#include <linux/regulator/driver.h>  #define SMBB_CHG_VMAX		0x040  #define SMBB_CHG_VSAFE		0x041 @@ -72,6 +73,8 @@  #define BTC_CTRL_HOT_EXT_N	BIT(0)  #define SMBB_USB_IMAX		0x344 +#define SMBB_USB_OTG_CTL	0x348 +#define OTG_CTL_EN		BIT(0)  #define SMBB_USB_ENUM_TIMER_STOP 0x34e  #define ENUM_TIMER_STOP		BIT(0)  #define SMBB_USB_SEC_ACCESS	0x3d0 @@ -125,6 +128,9 @@ struct smbb_charger {  	struct power_supply *dc_psy;  	struct power_supply *bat_psy;  	struct regmap *regmap; + +	struct regulator_desc otg_rdesc; +	struct regulator_dev *otg_reg;  };  static const unsigned int smbb_usb_extcon_cable[] = { @@ -378,7 +384,7 @@ 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, +	extcon_set_state_sync(chg->edev, EXTCON_USB,  				chg->status & STATUS_USBIN_VALID);  	power_supply_changed(chg->usb_psy); @@ -787,12 +793,56 @@ static const struct power_supply_desc dc_psy_desc = {  	.property_is_writeable = smbb_charger_writable_property,  }; +static int smbb_chg_otg_enable(struct regulator_dev *rdev) +{ +	struct smbb_charger *chg = rdev_get_drvdata(rdev); +	int rc; + +	rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, +				OTG_CTL_EN, OTG_CTL_EN); +	if (rc) +		dev_err(chg->dev, "failed to update OTG_CTL\n"); +	return rc; +} + +static int smbb_chg_otg_disable(struct regulator_dev *rdev) +{ +	struct smbb_charger *chg = rdev_get_drvdata(rdev); +	int rc; + +	rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, +				OTG_CTL_EN, 0); +	if (rc) +		dev_err(chg->dev, "failed to update OTG_CTL\n"); +	return rc; +} + +static int smbb_chg_otg_is_enabled(struct regulator_dev *rdev) +{ +	struct smbb_charger *chg = rdev_get_drvdata(rdev); +	unsigned int value = 0; +	int rc; + +	rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value); +	if (rc) +		dev_err(chg->dev, "failed to read OTG_CTL\n"); + +	return !!(value & OTG_CTL_EN); +} + +static const struct regulator_ops smbb_chg_otg_ops = { +	.enable = smbb_chg_otg_enable, +	.disable = smbb_chg_otg_disable, +	.is_enabled = smbb_chg_otg_is_enabled, +}; +  static int smbb_charger_probe(struct platform_device *pdev)  {  	struct power_supply_config bat_cfg = {};  	struct power_supply_config usb_cfg = {};  	struct power_supply_config dc_cfg = {};  	struct smbb_charger *chg; +	struct regulator_config config = { };  	int rc, i;  	chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); @@ -905,6 +955,26 @@ static int smbb_charger_probe(struct platform_device *pdev)  		}  	} +	/* +	 * otg regulator is used to control VBUS voltage direction +	 * when USB switches between host and gadget mode +	 */ +	chg->otg_rdesc.id = -1; +	chg->otg_rdesc.name = "otg-vbus"; +	chg->otg_rdesc.ops = &smbb_chg_otg_ops; +	chg->otg_rdesc.owner = THIS_MODULE; +	chg->otg_rdesc.type = REGULATOR_VOLTAGE; +	chg->otg_rdesc.supply_name = "usb-otg-in"; +	chg->otg_rdesc.of_match = "otg-vbus"; + +	config.dev = &pdev->dev; +	config.driver_data = chg; + +	chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc, +					       &config); +	if (IS_ERR(chg->otg_reg)) +		return PTR_ERR(chg->otg_reg); +  	chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,  			"qcom,jeita-extended-temp-range"); diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c new file mode 100644 index 000000000000..353765a5f44c --- /dev/null +++ b/drivers/power/supply/sbs-charger.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016, Prodys S.L. + * + * 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. + * + * This adds support for sbs-charger compilant chips as defined here: + * http://sbs-forum.org/specs/sbc110.pdf + * + * Implemetation based on sbs-battery.c + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/power_supply.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/regmap.h> +#include <linux/of_gpio.h> +#include <linux/bitops.h> + +#define SBS_CHARGER_REG_SPEC_INFO		0x11 +#define SBS_CHARGER_REG_STATUS			0x13 +#define SBS_CHARGER_REG_ALARM_WARNING		0x16 + +#define SBS_CHARGER_STATUS_CHARGE_INHIBITED	BIT(1) +#define SBS_CHARGER_STATUS_RES_COLD		BIT(9) +#define SBS_CHARGER_STATUS_RES_HOT		BIT(10) +#define SBS_CHARGER_STATUS_BATTERY_PRESENT	BIT(14) +#define SBS_CHARGER_STATUS_AC_PRESENT		BIT(15) + +#define SBS_CHARGER_POLL_TIME			500 + +struct sbs_info { +	struct i2c_client		*client; +	struct power_supply		*power_supply; +	struct regmap			*regmap; +	struct delayed_work		work; +	unsigned int			last_state; +}; + +static int sbs_get_property(struct power_supply *psy, +			    enum power_supply_property psp, +			    union power_supply_propval *val) +{ +	struct sbs_info *chip = power_supply_get_drvdata(psy); +	unsigned int reg; + +	reg = chip->last_state; + +	switch (psp) { +	case POWER_SUPPLY_PROP_PRESENT: +		val->intval = !!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT); +		break; + +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = !!(reg & SBS_CHARGER_STATUS_AC_PRESENT); +		break; + +	case POWER_SUPPLY_PROP_STATUS: +		val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + +		if (!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT)) +			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; +		else if (reg & SBS_CHARGER_STATUS_AC_PRESENT && +			 !(reg & SBS_CHARGER_STATUS_CHARGE_INHIBITED)) +			val->intval = POWER_SUPPLY_STATUS_CHARGING; +		else +			val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + +		break; + +	case POWER_SUPPLY_PROP_HEALTH: +		if (reg & SBS_CHARGER_STATUS_RES_COLD) +			val->intval = POWER_SUPPLY_HEALTH_COLD; +		if (reg & SBS_CHARGER_STATUS_RES_HOT) +			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; +		else +			val->intval = POWER_SUPPLY_HEALTH_GOOD; + +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int sbs_check_state(struct sbs_info *chip) +{ +	unsigned int reg; +	int ret; + +	ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, ®); +	if (!ret && reg != chip->last_state) { +		chip->last_state = reg; +		power_supply_changed(chip->power_supply); +		return 1; +	} + +	return 0; +} + +static void sbs_delayed_work(struct work_struct *work) +{ +	struct sbs_info *chip = container_of(work, struct sbs_info, work.work); + +	sbs_check_state(chip); + +	schedule_delayed_work(&chip->work, +			      msecs_to_jiffies(SBS_CHARGER_POLL_TIME)); +} + +static irqreturn_t sbs_irq_thread(int irq, void *data) +{ +	struct sbs_info *chip = data; +	int ret; + +	ret = sbs_check_state(chip); + +	return ret ? IRQ_HANDLED : IRQ_NONE; +} + +static enum power_supply_property sbs_properties[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_HEALTH, +}; + +static bool sbs_readable_reg(struct device *dev, unsigned int reg) +{ +	if (reg < SBS_CHARGER_REG_SPEC_INFO) +		return false; +	else +		return true; +} + +static bool sbs_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case SBS_CHARGER_REG_STATUS: +		return true; +	} + +	return false; +} + +static const struct regmap_config sbs_regmap = { +	.reg_bits	= 8, +	.val_bits	= 16, +	.max_register	= SBS_CHARGER_REG_ALARM_WARNING, +	.readable_reg	= sbs_readable_reg, +	.volatile_reg	= sbs_volatile_reg, +	.val_format_endian = REGMAP_ENDIAN_LITTLE, /* since based on SMBus */ +}; + +static const struct power_supply_desc sbs_desc = { +	.name = "sbs-charger", +	.type = POWER_SUPPLY_TYPE_MAINS, +	.properties = sbs_properties, +	.num_properties = ARRAY_SIZE(sbs_properties), +	.get_property = sbs_get_property, +}; + +static int sbs_probe(struct i2c_client *client, +		     const struct i2c_device_id *id) +{ +	struct power_supply_config psy_cfg = {}; +	struct sbs_info *chip; +	int ret, val; + +	chip = devm_kzalloc(&client->dev, sizeof(struct sbs_info), GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	chip->client = client; +	psy_cfg.of_node = client->dev.of_node; +	psy_cfg.drv_data = chip; + +	i2c_set_clientdata(client, chip); + +	chip->regmap = devm_regmap_init_i2c(client, &sbs_regmap); +	if (IS_ERR(chip->regmap)) +		return PTR_ERR(chip->regmap); + +	/* +	 * Before we register, we need to make sure we can actually talk +	 * to the battery. +	 */ +	ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, &val); +	if (ret) { +		dev_err(&client->dev, "Failed to get device status\n"); +		return ret; +	} +	chip->last_state = val; + +	chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc, +							&psy_cfg); +	if (IS_ERR(chip->power_supply)) { +		dev_err(&client->dev, "Failed to register power supply\n"); +		return PTR_ERR(chip->power_supply); +	} + +	/* +	 * The sbs-charger spec doesn't impose the use of an interrupt. So in +	 * the case it wasn't provided we use polling in order get the charger's +	 * status. +	 */ +	if (client->irq) { +		ret = devm_request_threaded_irq(&client->dev, client->irq, +					NULL, sbs_irq_thread, +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					dev_name(&client->dev), chip); +		if (ret) { +			dev_err(&client->dev, "Failed to request irq, %d\n", ret); +			return ret; +		} +	} else { +		INIT_DELAYED_WORK(&chip->work, sbs_delayed_work); +		schedule_delayed_work(&chip->work, +				      msecs_to_jiffies(SBS_CHARGER_POLL_TIME)); +	} + +	dev_info(&client->dev, +		 "%s: smart charger device registered\n", client->name); + +	return 0; +} + +static int sbs_remove(struct i2c_client *client) +{ +	struct sbs_info *chip = i2c_get_clientdata(client); + +	cancel_delayed_work_sync(&chip->work); + +	return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sbs_dt_ids[] = { +	{ .compatible = "sbs,sbs-charger" }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, sbs_dt_ids); +#endif + +static const struct i2c_device_id sbs_id[] = { +	{ "sbs-charger", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, sbs_id); + +static struct i2c_driver sbs_driver = { +	.probe		= sbs_probe, +	.remove		= sbs_remove, +	.id_table	= sbs_id, +	.driver = { +		.name	= "sbs-charger", +		.of_match_table = of_match_ptr(sbs_dt_ids), +	}, +}; +module_i2c_driver(sbs_driver); + +MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>"); +MODULE_DESCRIPTION("SBS smart charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c index 9fd019f9b88c..29b61e81b385 100644 --- a/drivers/power/supply/tps65217_charger.c +++ b/drivers/power/supply/tps65217_charger.c @@ -35,22 +35,22 @@  #include <linux/mfd/core.h>  #include <linux/mfd/tps65217.h> +#define CHARGER_STATUS_PRESENT	(TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR) +#define NUM_CHARGER_IRQS	2  #define POLL_INTERVAL		(HZ * 2)  struct tps65217_charger {  	struct tps65217 *tps;  	struct device *dev; -	struct power_supply *ac; +	struct power_supply *psy; -	int	ac_online; -	int	prev_ac_online; +	int	online; +	int	prev_online;  	struct task_struct	*poll_task; - -	int	irq;  }; -static enum power_supply_property tps65217_ac_props[] = { +static enum power_supply_property tps65217_charger_props[] = {  	POWER_SUPPLY_PROP_ONLINE,  }; @@ -95,7 +95,7 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)  	int ret;  	/* charger already enabled */ -	if (charger->ac_online) +	if (charger->online)  		return 0;  	dev_dbg(charger->dev, "%s: enable charging\n", __func__); @@ -110,19 +110,19 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)  		return ret;  	} -	charger->ac_online = 1; +	charger->online = 1;  	return 0;  } -static int tps65217_ac_get_property(struct power_supply *psy, -			enum power_supply_property psp, -			union power_supply_propval *val) +static int tps65217_charger_get_property(struct power_supply *psy, +					 enum power_supply_property psp, +					 union power_supply_propval *val)  {  	struct tps65217_charger *charger = power_supply_get_drvdata(psy);  	if (psp == POWER_SUPPLY_PROP_ONLINE) { -		val->intval = charger->ac_online; +		val->intval = charger->online;  		return 0;  	}  	return -EINVAL; @@ -133,7 +133,7 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)  	int ret, val;  	struct tps65217_charger *charger = dev; -	charger->prev_ac_online = charger->ac_online; +	charger->prev_online = charger->online;  	ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);  	if (ret < 0) { @@ -144,8 +144,8 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)  	dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val); -	/* check for AC status bit */ -	if (val & TPS65217_STATUS_ACPWR) { +	/* check for charger status bit */ +	if (val & CHARGER_STATUS_PRESENT) {  		ret = tps65217_enable_charging(charger);  		if (ret) {  			dev_err(charger->dev, @@ -153,11 +153,11 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)  			return IRQ_HANDLED;  		}  	} else { -		charger->ac_online = 0; +		charger->online = 0;  	} -	if (charger->prev_ac_online != charger->ac_online) -		power_supply_changed(charger->ac); +	if (charger->prev_online != charger->online) +		power_supply_changed(charger->psy);  	ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);  	if (ret < 0) { @@ -188,11 +188,11 @@ static int tps65217_charger_poll_task(void *data)  }  static const struct power_supply_desc tps65217_charger_desc = { -	.name			= "tps65217-ac", +	.name			= "tps65217-charger",  	.type			= POWER_SUPPLY_TYPE_MAINS, -	.get_property		= tps65217_ac_get_property, -	.properties		= tps65217_ac_props, -	.num_properties		= ARRAY_SIZE(tps65217_ac_props), +	.get_property		= tps65217_charger_get_property, +	.properties		= tps65217_charger_props, +	.num_properties		= ARRAY_SIZE(tps65217_charger_props),  };  static int tps65217_charger_probe(struct platform_device *pdev) @@ -200,8 +200,10 @@ static int tps65217_charger_probe(struct platform_device *pdev)  	struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);  	struct tps65217_charger *charger;  	struct power_supply_config cfg = {}; -	int irq; +	struct task_struct *poll_task; +	int irq[NUM_CHARGER_IRQS];  	int ret; +	int i;  	dev_dbg(&pdev->dev, "%s\n", __func__); @@ -216,18 +218,16 @@ static int tps65217_charger_probe(struct platform_device *pdev)  	cfg.of_node = pdev->dev.of_node;  	cfg.drv_data = charger; -	charger->ac = devm_power_supply_register(&pdev->dev, -						 &tps65217_charger_desc, -						 &cfg); -	if (IS_ERR(charger->ac)) { +	charger->psy = devm_power_supply_register(&pdev->dev, +						  &tps65217_charger_desc, +						  &cfg); +	if (IS_ERR(charger->psy)) {  		dev_err(&pdev->dev, "failed: power supply register\n"); -		return PTR_ERR(charger->ac); +		return PTR_ERR(charger->psy);  	} -	irq = platform_get_irq_byname(pdev, "AC"); -	if (irq < 0) -		irq = -ENXIO; -	charger->irq = irq; +	irq[0] = platform_get_irq_byname(pdev, "USB"); +	irq[1] = platform_get_irq_byname(pdev, "AC");  	ret = tps65217_config_charger(charger);  	if (ret < 0) { @@ -235,29 +235,36 @@ static int tps65217_charger_probe(struct platform_device *pdev)  		return ret;  	} -	if (irq != -ENXIO) { -		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, +	/* Create a polling thread if an interrupt is invalid */ +	if (irq[0] < 0 || irq[1] < 0) { +		poll_task = kthread_run(tps65217_charger_poll_task, +					charger, "ktps65217charger"); +		if (IS_ERR(poll_task)) { +			ret = PTR_ERR(poll_task); +			dev_err(charger->dev, +				"Unable to run kthread err %d\n", ret); +			return ret; +		} + +		charger->poll_task = poll_task; +		return 0; +	} + +	/* Create IRQ threads for charger interrupts */ +	for (i = 0; i < NUM_CHARGER_IRQS; i++) { +		ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,  						tps65217_charger_irq,  						0, "tps65217-charger",  						charger);  		if (ret) {  			dev_err(charger->dev, -				"Unable to register irq %d err %d\n", irq, +				"Unable to register irq %d err %d\n", irq[i],  				ret);  			return ret;  		}  		/* Check current state */ -		tps65217_charger_irq(irq, charger); -	} else { -		charger->poll_task = kthread_run(tps65217_charger_poll_task, -						charger, "ktps65217charger"); -		if (IS_ERR(charger->poll_task)) { -			ret = PTR_ERR(charger->poll_task); -			dev_err(charger->dev, -				"Unable to run kthread err %d\n", ret); -			return ret; -		} +		tps65217_charger_irq(-1, charger);  	}  	return 0; @@ -267,7 +274,7 @@ static int tps65217_charger_remove(struct platform_device *pdev)  {  	struct tps65217_charger *charger = platform_get_drvdata(pdev); -	if (charger->irq == -ENXIO) +	if (charger->poll_task)  		kthread_stop(charger->poll_task);  	return 0; diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c index e3edb31ac880..bd4f66651513 100644 --- a/drivers/power/supply/wm97xx_battery.c +++ b/drivers/power/supply/wm97xx_battery.c @@ -175,11 +175,6 @@ static int wm97xx_bat_probe(struct platform_device *dev)  	if (dev->id != -1)  		return -EINVAL; -	if (!pdata) { -		dev_err(&dev->dev, "No platform_data supplied\n"); -		return -EINVAL; -	} -  	if (gpio_is_valid(pdata->charge_gpio)) {  		ret = gpio_request(pdata->charge_gpio, "BATT CHRG");  		if (ret) diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index a4860bc9b73d..f848ee86a339 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -13,7 +13,7 @@  #include <linux/regmap.h> -enum { +enum axp20x_variants {  	AXP152_ID = 0,  	AXP202_ID,  	AXP209_ID, @@ -532,35 +532,6 @@ struct axp20x_dev {  	const struct regmap_irq_chip	*regmap_irq_chip;  }; -#define BATTID_LEN				64 -#define OCV_CURVE_SIZE			32 -#define MAX_THERM_CURVE_SIZE	25 -#define PD_DEF_MIN_TEMP			0 -#define PD_DEF_MAX_TEMP			55 - -struct axp20x_fg_pdata { -	char battid[BATTID_LEN + 1]; -	int design_cap; -	int min_volt; -	int max_volt; -	int max_temp; -	int min_temp; -	int cap1; -	int cap0; -	int rdc1; -	int rdc0; -	int ocv_curve[OCV_CURVE_SIZE]; -	int tcsz; -	int thermistor_curve[MAX_THERM_CURVE_SIZE][2]; -}; - -struct axp20x_chrg_pdata { -	int max_cc; -	int max_cv; -	int def_cc; -	int def_cv; -}; -  struct axp288_extcon_pdata {  	/* GPIO pin control to switch D+/D- lines b/w PMIC and SOC */  	struct gpio_desc *gpio_mux_cntl; diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index bed9557b69e7..b312bcef53da 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -4,8 +4,16 @@  enum bq27xxx_chip {  	BQ27000 = 1, /* bq27000, bq27200 */  	BQ27010, /* bq27010, bq27210 */ -	BQ27500, /* bq27500 */ -	BQ27510, /* bq27510, bq27520 */ +	BQ2750X, /* bq27500 deprecated alias */ +	BQ2751X, /* bq27510, bq27520 deprecated alias */ +	BQ27500, /* bq27500/1 */ +	BQ27510G1, /* bq27510G1 */ +	BQ27510G2, /* bq27510G2 */ +	BQ27510G3, /* bq27510G3 */ +	BQ27520G1, /* bq27520G1 */ +	BQ27520G2, /* bq27520G2 */ +	BQ27520G3, /* bq27520G3 */ +	BQ27520G4, /* bq27520G4 */  	BQ27530, /* bq27530, bq27531 */  	BQ27541, /* bq27541, bq27542, bq27546, bq27742 */  	BQ27545, /* bq27545 */ diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h index dc10c52e0e91..393362bdb860 100644 --- a/include/soc/at91/at91sam9_ddrsdr.h +++ b/include/soc/at91/at91sam9_ddrsdr.h @@ -81,6 +81,7 @@  #define			AT91_DDRSDRC_LPCB_POWER_DOWN		2  #define			AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN	3  #define		AT91_DDRSDRC_CLKFR	(1 << 2)	/* Clock Frozen */ +#define		AT91_DDRSDRC_LPDDR2_PWOFF	(1 << 3)	/* LPDDR Power Off */  #define		AT91_DDRSDRC_PASR	(7 << 4)	/* Partial Array Self Refresh */  #define		AT91_DDRSDRC_TCSR	(3 << 8)	/* Temperature Compensated Self Refresh */  #define		AT91_DDRSDRC_DS		(3 << 10)	/* Drive Strength */ @@ -96,7 +97,9 @@  #define			AT91_DDRSDRC_MD_SDR		0  #define			AT91_DDRSDRC_MD_LOW_POWER_SDR	1  #define			AT91_DDRSDRC_MD_LOW_POWER_DDR	3 +#define			AT91_DDRSDRC_MD_LPDDR3		5  #define			AT91_DDRSDRC_MD_DDR2		6	/* [SAM9 Only] */ +#define			AT91_DDRSDRC_MD_LPDDR2		7  #define		AT91_DDRSDRC_DBW	(1 << 4)		/* Data Bus Width */  #define			AT91_DDRSDRC_DBW_32BITS		(0 <<  4)  #define			AT91_DDRSDRC_DBW_16BITS		(1 <<  4)  | 
