Commit c9b9f207 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 345fb0a9 744cc304
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";
};
};
......@@ -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.
......
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>;
};
......@@ -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 {};
};
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>;
};
......@@ -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:
......
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>;
};
};
......@@ -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
......
......@@ -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", },
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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
......
......@@ -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
......
......@@ -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;
}
/**
......
/*
* 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, &reg);
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, &reg);
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, &reg);
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");
......@@ -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);
......
This diff is collapsed.
This diff is collapsed.
......@@ -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) {
......
......@@ -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 */
......
......@@ -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,
};
......
This diff is collapsed.
......@@ -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" },
......
......@@ -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;
}
......
This diff is collapsed.
/*
* 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");
......@@ -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,
};
......
......@@ -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);
......
......@@ -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");
......
This diff is collapsed.
This diff is collapsed.
......@@ -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)
......
......@@ -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;
......
......@@ -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 */
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment