power supply and reset changes for the v4.11 series
* 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAliqc7UACgkQ2O7X88g7 +pqV9g//T96CM/O6qHx5VPOAYrHP9pG+iOXjqMQi0oHqW4ggPMulGCLaD7rKpN9I 2pxEJmw9e+F9VAbaY6yFIldOuv/tIo/6Kpnq0VgzHvJoDsJzCbs70tRd4xyPesGL icrI9HZgWEj8jIcKCoBDWjoMHDJEj5igirzw2Rzx3rRcXvoDSrW097vWQyQOv8uN jmGHAsP/J3KaRZsUc5eHfHnbMd750ZWUjANMWcZwfOBkxyKEsiotXqeZqjy5VXyc EzrAvZ8/jZS2CdwgRZUYzPY+awCJqEqbqOx9jitXpO9zrcnOBeArbZDps2tCQxBB 8Ect4UgdkQ5LdHKYZKu9GkChCSOoJdT9wiXsKBATvk+/y2607dtAcVvuSCP7Ogu/ DWZan2oFIx+F15moPHPGKq8bCjwRGR1xFt0ENNpSEInVEG6q/qjpPx8QQvs5YhI0 4cJrm/ZrrdN61znfGDqm+vFqL2BOjiLpxzvOOg8ouTZ80NcrZl8oiAFargoD+rGG KnhRqeWVSKyC9TB01hh2ThQC8F7UHmhw7A10gcmLEx8xApweR8hYGQYtjCQT9Uqs r3biIyWDmEFgmp1J0iM8VJyNc/qcl8dcCCJb0qqfFhpqDoL+QoHLQo3yqM/1osS6 Bs0OZT3QKkHgsQSuDe/knWSr1wLtk/EAhVAolWfH5LztPPsaSrY= =rRsU -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
c9b9f207b9
|
@ -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";
|
||||||
|
};
|
||||||
|
};
|
|
@ -3,6 +3,11 @@ AXP20x USB power supply
|
||||||
Required Properties:
|
Required Properties:
|
||||||
-compatible: One of: "x-powers,axp202-usb-power-supply"
|
-compatible: One of: "x-powers,axp202-usb-power-supply"
|
||||||
"x-powers,axp221-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.
|
This node is a subnode of the axp20x PMIC.
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
|
@ -105,6 +105,22 @@ PROPERTIES
|
||||||
regulation must be done externally to fully comply with
|
regulation must be done externally to fully comply with
|
||||||
the JEITA safety guidelines if this flag is set.
|
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
|
EXAMPLE
|
||||||
charger@1000 {
|
charger@1000 {
|
||||||
compatible = "qcom,pm8941-charger";
|
compatible = "qcom,pm8941-charger";
|
||||||
|
@ -128,4 +144,7 @@ charger@1000 {
|
||||||
|
|
||||||
qcom,fast-charge-current-limit = <1000000>;
|
qcom,fast-charge-current-limit = <1000000>;
|
||||||
qcom,dc-charge-current-limit = <1000000>;
|
qcom,dc-charge-current-limit = <1000000>;
|
||||||
|
usb_otg_in-supply = <&pm8941_5vs1>;
|
||||||
|
|
||||||
|
otg-vbus {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
|
@ -8,8 +8,10 @@ Optional properties :
|
||||||
- interrupts : Specify the interrupt to be used to trigger when the AC
|
- interrupts : Specify the interrupt to be used to trigger when the AC
|
||||||
adapter is either plugged in or removed.
|
adapter is either plugged in or removed.
|
||||||
- ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter
|
- 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
|
status. This is a Host GPIO that is configured as an input and connected
|
||||||
connected to the bq24735.
|
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
|
- 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
|
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
|
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
|
- ti,external-control : Indicates that the charger is configured externally
|
||||||
and that the host should not attempt to enable/disable charging or set the
|
and that the host should not attempt to enable/disable charging or set the
|
||||||
charge voltage/current.
|
charge voltage/current.
|
||||||
|
- poll-interval : In case 'interrupts' is not specified, poll AC adapter
|
||||||
|
presence with this interval (milliseconds).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -32,7 +32,7 @@ config POWER_RESET_AT91_RESET
|
||||||
|
|
||||||
config POWER_RESET_AT91_SAMA5D2_SHDWC
|
config POWER_RESET_AT91_SAMA5D2_SHDWC
|
||||||
tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver"
|
tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver"
|
||||||
depends on ARCH_AT91 || COMPILE_TEST
|
depends on ARCH_AT91
|
||||||
default SOC_SAMA5
|
default SOC_SAMA5
|
||||||
help
|
help
|
||||||
This driver supports the alternate shutdown controller for some Atmel
|
This driver supports the alternate shutdown controller for some Atmel
|
||||||
|
|
|
@ -14,9 +14,12 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
|
|
||||||
|
#include <soc/at91/at91sam9_ddrsdr.h>
|
||||||
|
|
||||||
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
||||||
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
|
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
|
||||||
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
|
#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 void __iomem *at91_shdwc_base;
|
||||||
static struct clk *sclk;
|
static struct clk *sclk;
|
||||||
|
static void __iomem *mpddrc_base;
|
||||||
|
|
||||||
static void __init at91_wakeup_status(void)
|
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);
|
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)
|
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
|
||||||
{
|
{
|
||||||
const char *pm;
|
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)
|
static int __init at91_poweroff_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
struct device_node *np;
|
||||||
|
u32 ddr_type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __exit at91_poweroff_remove(struct platform_device *pdev)
|
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;
|
pm_power_off = NULL;
|
||||||
|
|
||||||
clk_disable_unprepare(sclk);
|
clk_disable_unprepare(sclk);
|
||||||
|
@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)
|
||||||
return 0;
|
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[] = {
|
static const struct of_device_id at91_poweroff_of_match[] = {
|
||||||
{ .compatible = "atmel,at91sam9260-shdwc", },
|
{ .compatible = "atmel,at91sam9260-shdwc", },
|
||||||
{ .compatible = "atmel,at91sam9rl-shdwc", },
|
{ .compatible = "atmel,at91sam9rl-shdwc", },
|
||||||
|
|
|
@ -134,6 +134,15 @@ static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
|
||||||
return NOTIFY_DONE;
|
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)
|
static void __init at91_reset_status(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
|
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,at91sam9260-rstc", .data = at91sam9260_restart },
|
||||||
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
|
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
|
||||||
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
|
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
|
||||||
|
{ .compatible = "atmel,samx7-rstc", .data = samx7_restart },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
|
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
|
||||||
|
@ -238,20 +248,12 @@ static int __exit at91_reset_remove(struct platform_device *pdev)
|
||||||
return 0;
|
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 = {
|
static struct platform_driver at91_reset_driver = {
|
||||||
.remove = __exit_p(at91_reset_remove),
|
.remove = __exit_p(at91_reset_remove),
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "at91-reset",
|
.name = "at91-reset",
|
||||||
.of_match_table = at91_reset_of_match,
|
.of_match_table = at91_reset_of_match,
|
||||||
},
|
},
|
||||||
.id_table = at91_reset_plat_match,
|
|
||||||
};
|
};
|
||||||
module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
|
module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,12 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
|
|
||||||
|
#include <soc/at91/at91sam9_ddrsdr.h>
|
||||||
|
|
||||||
#define SLOW_CLOCK_FREQ 32768
|
#define SLOW_CLOCK_FREQ 32768
|
||||||
|
|
||||||
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
||||||
|
@ -75,6 +78,7 @@ struct shdwc {
|
||||||
*/
|
*/
|
||||||
static struct shdwc *at91_shdwc;
|
static struct shdwc *at91_shdwc;
|
||||||
static struct clk *sclk;
|
static struct clk *sclk;
|
||||||
|
static void __iomem *mpddrc_base;
|
||||||
|
|
||||||
static const unsigned long long sdwc_dbc_period[] = {
|
static const unsigned long long sdwc_dbc_period[] = {
|
||||||
0, 3, 32, 512, 4096, 32768,
|
0, 3, 32, 512, 4096, 32768,
|
||||||
|
@ -108,6 +112,29 @@ static void at91_poweroff(void)
|
||||||
at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);
|
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,
|
static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
|
||||||
u32 in_period_us)
|
u32 in_period_us)
|
||||||
{
|
{
|
||||||
|
@ -212,6 +239,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
struct device_node *np;
|
||||||
|
u32 ddr_type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pdev->dev.of_node)
|
if (!pdev->dev.of_node)
|
||||||
|
@ -249,6 +278,23 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pm_power_off = at91_poweroff;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +302,8 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct shdwc *shdw = platform_get_drvdata(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;
|
pm_power_off = NULL;
|
||||||
|
|
||||||
/* Reset values to disable wake-up features */
|
/* 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
|
Say Y to include support for SBS battery driver for SBS-compliant
|
||||||
gas gauges.
|
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
|
config BATTERY_BQ27XXX
|
||||||
tristate "BQ27xxx battery driver"
|
tristate "BQ27xxx battery driver"
|
||||||
help
|
help
|
||||||
|
@ -214,6 +220,18 @@ config BATTERY_DA9150
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called da9150-fg.
|
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
|
config AXP288_CHARGER
|
||||||
tristate "X-Powers AXP288 Charger"
|
tristate "X-Powers AXP288 Charger"
|
||||||
depends on MFD_AXP20X && EXTCON_AXP288
|
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
|
This driver can be build as a module. If so, the module will be
|
||||||
called jz4740-battery.
|
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
|
config BATTERY_RX51
|
||||||
tristate "Nokia RX-51 (N900) battery driver"
|
tristate "Nokia RX-51 (N900) battery driver"
|
||||||
depends on TWL4030_MADC
|
depends on TWL4030_MADC
|
||||||
|
@ -370,6 +381,16 @@ config CHARGER_MAX14577
|
||||||
Say Y to enable support for the battery charger control sysfs and
|
Say Y to enable support for the battery charger control sysfs and
|
||||||
platform data of MAX14577/77836 MUICs.
|
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
|
config CHARGER_MAX77693
|
||||||
tristate "Maxim MAX77693 battery charger driver"
|
tristate "Maxim MAX77693 battery charger driver"
|
||||||
depends on MFD_MAX77693
|
depends on MFD_MAX77693
|
||||||
|
@ -395,6 +416,7 @@ config CHARGER_QCOM_SMBB
|
||||||
depends on MFD_SPMI_PMIC || COMPILE_TEST
|
depends on MFD_SPMI_PMIC || COMPILE_TEST
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on EXTCON
|
depends on EXTCON
|
||||||
|
depends on REGULATOR
|
||||||
help
|
help
|
||||||
Say Y to include support for the Switch-Mode Battery Charger and
|
Say Y to include support for the Switch-Mode Battery Charger and
|
||||||
Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger
|
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_88PM860X) += 88pm860x_battery.o
|
||||||
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.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_DS2760) += ds2760_battery.o
|
||||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||||
obj-$(CONFIG_BATTERY_DS2781) += ds2781_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_IPAQ_MICRO) += ipaq_micro_battery.o
|
||||||
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
||||||
obj-$(CONFIG_BATTERY_SBS) += sbs-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) += bq27xxx_battery.o
|
||||||
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
|
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
|
||||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.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_88PM860X) += 88pm860x_charger.o
|
||||||
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
||||||
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.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_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_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
|
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_GPIO) += gpio-charger.o
|
||||||
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
||||||
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.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_MAX77693) += max77693_charger.o
|
||||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||||
|
|
|
@ -76,8 +76,8 @@ struct ab8500_btemp_ranges {
|
||||||
* @dev: Pointer to the structure device
|
* @dev: Pointer to the structure device
|
||||||
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
|
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
|
||||||
* @curr_source: What current source we use, in uA
|
* @curr_source: What current source we use, in uA
|
||||||
* @bat_temp: Dispatched battery temperature in degree Celcius
|
* @bat_temp: Dispatched battery temperature in degree Celsius
|
||||||
* @prev_bat_temp Last measured battery temperature in degree Celcius
|
* @prev_bat_temp Last measured battery temperature in degree Celsius
|
||||||
* @parent: Pointer to the struct ab8500
|
* @parent: Pointer to the struct ab8500
|
||||||
* @gpadc: Pointer to the struct gpadc
|
* @gpadc: Pointer to the struct gpadc
|
||||||
* @fg: Pointer to the struct fg
|
* @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 *ab8500_btemp_get(void)
|
||||||
{
|
{
|
||||||
struct ab8500_btemp *btemp;
|
return list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
|
||||||
btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
|
|
||||||
|
|
||||||
return btemp;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ab8500_btemp_get);
|
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
|
* @tbl_size: size of the resistance to temperature table
|
||||||
* @res: resistance to calculate the temperature from
|
* @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.
|
* based on the NTC resistance.
|
||||||
*/
|
*/
|
||||||
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
||||||
const struct abx500_res_to_temp *tbl, int tbl_size, int res)
|
const struct abx500_res_to_temp *tbl, int tbl_size, int res)
|
||||||
{
|
{
|
||||||
int i, temp;
|
int i;
|
||||||
/*
|
/*
|
||||||
* Calculate the formula for the straight line
|
* Calculate the formula for the straight line
|
||||||
* Simple interpolation if we are within
|
* Simple interpolation if we are within
|
||||||
|
@ -488,9 +485,8 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
||||||
i++;
|
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);
|
(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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");
|
|
@ -17,10 +17,12 @@
|
||||||
#include <linux/mfd/axp20x.h>
|
#include <linux/mfd/axp20x.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/iio/consumer.h>
|
||||||
|
|
||||||
#define DRVNAME "axp20x-usb-power-supply"
|
#define DRVNAME "axp20x-usb-power-supply"
|
||||||
|
|
||||||
|
@ -30,6 +32,8 @@
|
||||||
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
|
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
|
||||||
|
|
||||||
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
|
#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_VBUS_CLIMIT_MASK 3
|
||||||
#define AXP20X_VBUC_CLIMIT_900mA 0
|
#define AXP20X_VBUC_CLIMIT_900mA 0
|
||||||
#define AXP20X_VBUC_CLIMIT_500mA 1
|
#define AXP20X_VBUC_CLIMIT_500mA 1
|
||||||
|
@ -45,6 +49,9 @@ struct axp20x_usb_power {
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct power_supply *supply;
|
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)
|
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);
|
val->intval = AXP20X_VBUS_VHOLD_uV(v);
|
||||||
return 0;
|
return 0;
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
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,
|
ret = axp20x_read_variable_width(power->regmap,
|
||||||
AXP20X_VBUS_V_ADC_H, 12);
|
AXP20X_VBUS_V_ADC_H, 12);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -86,12 +107,10 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||||
|
|
||||||
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
|
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
|
||||||
case AXP20X_VBUC_CLIMIT_100mA:
|
case AXP20X_VBUC_CLIMIT_100mA:
|
||||||
if (of_device_is_compatible(power->np,
|
if (power->axp20x_id == AXP221_ID)
|
||||||
"x-powers,axp202-usb-power-supply")) {
|
|
||||||
val->intval = 100000;
|
|
||||||
} else {
|
|
||||||
val->intval = -1; /* No 100mA limit */
|
val->intval = -1; /* No 100mA limit */
|
||||||
}
|
else
|
||||||
|
val->intval = 100000;
|
||||||
break;
|
break;
|
||||||
case AXP20X_VBUC_CLIMIT_500mA:
|
case AXP20X_VBUC_CLIMIT_500mA:
|
||||||
val->intval = 500000;
|
val->intval = 500000;
|
||||||
|
@ -105,6 +124,20 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
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,
|
ret = axp20x_read_variable_width(power->regmap,
|
||||||
AXP20X_VBUS_I_ADC_H, 12);
|
AXP20X_VBUS_I_ADC_H, 12);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -130,8 +163,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||||
|
|
||||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||||
|
|
||||||
if (of_device_is_compatible(power->np,
|
if (power->axp20x_id == AXP202_ID) {
|
||||||
"x-powers,axp202-usb-power-supply")) {
|
|
||||||
ret = regmap_read(power->regmap,
|
ret = regmap_read(power->regmap,
|
||||||
AXP20X_USB_OTG_STATUS, &v);
|
AXP20X_USB_OTG_STATUS, &v);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -155,6 +187,81 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||||
return 0;
|
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[] = {
|
static enum power_supply_property axp20x_usb_power_properties[] = {
|
||||||
POWER_SUPPLY_PROP_HEALTH,
|
POWER_SUPPLY_PROP_HEALTH,
|
||||||
POWER_SUPPLY_PROP_PRESENT,
|
POWER_SUPPLY_PROP_PRESENT,
|
||||||
|
@ -178,7 +285,9 @@ static const struct power_supply_desc axp20x_usb_power_desc = {
|
||||||
.type = POWER_SUPPLY_TYPE_USB,
|
.type = POWER_SUPPLY_TYPE_USB,
|
||||||
.properties = axp20x_usb_power_properties,
|
.properties = axp20x_usb_power_properties,
|
||||||
.num_properties = ARRAY_SIZE(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,
|
.get_property = axp20x_usb_power_get_property,
|
||||||
|
.set_property = axp20x_usb_power_set_property,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct power_supply_desc axp22x_usb_power_desc = {
|
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,
|
.type = POWER_SUPPLY_TYPE_USB,
|
||||||
.properties = axp22x_usb_power_properties,
|
.properties = axp22x_usb_power_properties,
|
||||||
.num_properties = ARRAY_SIZE(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,
|
.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)
|
static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
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)
|
if (!power)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
power->axp20x_id = (enum axp20x_variants)of_device_get_match_data(
|
||||||
|
&pdev->dev);
|
||||||
|
|
||||||
power->np = pdev->dev.of_node;
|
power->np = pdev->dev.of_node;
|
||||||
power->regmap = axp20x->regmap;
|
power->regmap = axp20x->regmap;
|
||||||
|
|
||||||
if (of_device_is_compatible(power->np,
|
if (power->axp20x_id == AXP202_ID) {
|
||||||
"x-powers,axp202-usb-power-supply")) {
|
|
||||||
/* Enable vbus valid checking */
|
/* Enable vbus valid checking */
|
||||||
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
|
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
|
||||||
AXP20X_VBUS_MON_VBUS_VALID,
|
AXP20X_VBUS_MON_VBUS_VALID,
|
||||||
|
@ -226,17 +369,18 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Enable vbus voltage and current measurement */
|
if (IS_ENABLED(CONFIG_AXP20X_ADC))
|
||||||
ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
|
ret = configure_iio_channels(pdev, power);
|
||||||
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
|
else
|
||||||
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
|
ret = configure_adc_registers(power);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
usb_power_desc = &axp20x_usb_power_desc;
|
usb_power_desc = &axp20x_usb_power_desc;
|
||||||
irq_names = axp20x_irq_names;
|
irq_names = axp20x_irq_names;
|
||||||
} else if (of_device_is_compatible(power->np,
|
} else if (power->axp20x_id == AXP221_ID ||
|
||||||
"x-powers,axp221-usb-power-supply")) {
|
power->axp20x_id == AXP223_ID) {
|
||||||
usb_power_desc = &axp22x_usb_power_desc;
|
usb_power_desc = &axp22x_usb_power_desc;
|
||||||
irq_names = axp22x_irq_names;
|
irq_names = axp22x_irq_names;
|
||||||
} else {
|
} 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[] = {
|
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);
|
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
|
||||||
|
|
||||||
|
|
|
@ -90,20 +90,6 @@
|
||||||
#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
|
#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
|
||||||
#define CHRG_VHTFC_45C 0x1F /* 45 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 FG_CNTL_OCV_ADJ_EN (1 << 3)
|
||||||
|
|
||||||
#define CV_4100MV 4100 /* 4100mV */
|
#define CV_4100MV 4100 /* 4100mV */
|
||||||
|
@ -127,6 +113,10 @@
|
||||||
#define ILIM_3000MA 3000 /* 3000mA */
|
#define ILIM_3000MA 3000 /* 3000mA */
|
||||||
|
|
||||||
#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
|
#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 {
|
enum {
|
||||||
VBUS_OV_IRQ = 0,
|
VBUS_OV_IRQ = 0,
|
||||||
|
@ -143,7 +133,6 @@ enum {
|
||||||
|
|
||||||
struct axp288_chrg_info {
|
struct axp288_chrg_info {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct axp20x_chrg_pdata *pdata;
|
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct regmap_irq_chip_data *regmap_irqc;
|
struct regmap_irq_chip_data *regmap_irqc;
|
||||||
int irq[CHRG_INTR_END];
|
int irq[CHRG_INTR_END];
|
||||||
|
@ -163,20 +152,16 @@ struct axp288_chrg_info {
|
||||||
struct extcon_dev *edev;
|
struct extcon_dev *edev;
|
||||||
bool connected;
|
bool connected;
|
||||||
enum power_supply_type chg_type;
|
enum power_supply_type chg_type;
|
||||||
struct notifier_block nb;
|
struct notifier_block nb[ARRAY_SIZE(cable_ids)];
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
} cable;
|
} cable;
|
||||||
|
|
||||||
int health;
|
|
||||||
int inlmt;
|
int inlmt;
|
||||||
int cc;
|
int cc;
|
||||||
int cv;
|
int cv;
|
||||||
int max_cc;
|
int max_cc;
|
||||||
int max_cv;
|
int max_cv;
|
||||||
bool online;
|
int is_charger_enabled;
|
||||||
bool present;
|
|
||||||
bool enable_charger;
|
|
||||||
bool is_charger_enabled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
|
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;
|
int ret;
|
||||||
|
|
||||||
|
if ((int)enable == info->is_charger_enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
|
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
|
||||||
CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
|
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);
|
ret = axp288_charger_is_present(info);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto psy_get_prop_fail;
|
goto psy_get_prop_fail;
|
||||||
info->present = ret;
|
val->intval = ret;
|
||||||
val->intval = info->present;
|
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_PROP_ONLINE:
|
case POWER_SUPPLY_PROP_ONLINE:
|
||||||
/* Check for OTG case first */
|
/* 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);
|
ret = axp288_charger_is_online(info);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto psy_get_prop_fail;
|
goto psy_get_prop_fail;
|
||||||
info->online = ret;
|
val->intval = ret;
|
||||||
val->intval = info->online;
|
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_PROP_HEALTH:
|
case POWER_SUPPLY_PROP_HEALTH:
|
||||||
val->intval = axp288_get_charger_health(info);
|
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 =
|
struct axp288_chrg_info *info =
|
||||||
container_of(work, struct axp288_chrg_info, cable.work);
|
container_of(work, struct axp288_chrg_info, cable.work);
|
||||||
int ret, current_limit;
|
int ret, current_limit;
|
||||||
bool changed = false;
|
|
||||||
struct extcon_dev *edev = info->cable.edev;
|
struct extcon_dev *edev = info->cable.edev;
|
||||||
bool old_connected = info->cable.connected;
|
bool old_connected = info->cable.connected;
|
||||||
|
enum power_supply_type old_chg_type = info->cable.chg_type;
|
||||||
|
|
||||||
/* Determine cable/charger 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");
|
dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
|
||||||
info->cable.connected = true;
|
info->cable.connected = true;
|
||||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
|
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");
|
dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
|
||||||
info->cable.connected = true;
|
info->cable.connected = true;
|
||||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
|
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");
|
dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
|
||||||
info->cable.connected = true;
|
info->cable.connected = true;
|
||||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
|
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 */
|
/* Cable status changed */
|
||||||
if (old_connected != info->cable.connected)
|
if (old_connected == info->cable.connected &&
|
||||||
changed = true;
|
old_chg_type == info->cable.chg_type)
|
||||||
|
|
||||||
if (!changed)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&info->lock);
|
mutex_lock(&info->lock);
|
||||||
|
|
||||||
if (info->is_charger_enabled && !info->cable.connected) {
|
if (info->cable.connected) {
|
||||||
info->enable_charger = false;
|
axp288_charger_enable_charger(info, false);
|
||||||
ret = axp288_charger_enable_charger(info, info->enable_charger);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_err(&info->pdev->dev,
|
|
||||||
"cannot disable charger (%d)", ret);
|
|
||||||
|
|
||||||
} else if (!info->is_charger_enabled && info->cable.connected) {
|
|
||||||
switch (info->cable.chg_type) {
|
switch (info->cable.chg_type) {
|
||||||
case POWER_SUPPLY_TYPE_USB:
|
case POWER_SUPPLY_TYPE_USB:
|
||||||
current_limit = ILIM_500MA;
|
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 */
|
/* Set vbus current limit first, then enable charger */
|
||||||
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
|
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,
|
dev_err(&info->pdev->dev,
|
||||||
"error setting current limit (%d)", ret);
|
"error setting current limit (%d)", ret);
|
||||||
} else {
|
} else {
|
||||||
info->enable_charger = (current_limit > 0);
|
axp288_charger_enable_charger(info, false);
|
||||||
ret = axp288_charger_enable_charger(info,
|
|
||||||
info->enable_charger);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_err(&info->pdev->dev,
|
|
||||||
"cannot enable charger (%d)", ret);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed)
|
|
||||||
info->health = axp288_get_charger_health(info);
|
|
||||||
|
|
||||||
mutex_unlock(&info->lock);
|
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 =
|
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);
|
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;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +664,17 @@ static void axp288_charger_otg_evt_worker(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct axp288_chrg_info *info =
|
struct axp288_chrg_info *info =
|
||||||
container_of(work, struct axp288_chrg_info, otg.work);
|
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 */
|
/* Disable VBUS path before enabling the 5V boost */
|
||||||
ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
|
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 =
|
struct axp288_chrg_info *info =
|
||||||
container_of(nb, struct axp288_chrg_info, otg.id_nb);
|
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);
|
schedule_work(&info->otg.work);
|
||||||
|
|
||||||
return NOTIFY_OK;
|
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;
|
int ret, cc, cv;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
/* Program temperature thresholds */
|
/* Program temperature thresholds */
|
||||||
ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
|
ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||||
AXP20X_V_LTF_CHRG, ret);
|
AXP20X_V_LTF_CHRG, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
|
ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||||
AXP20X_V_HTF_CHRG, ret);
|
AXP20X_V_HTF_CHRG, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Do not turn-off charger o/p after charge cycle ends */
|
/* Do not turn-off charger o/p after charge cycle ends */
|
||||||
ret = regmap_update_bits(info->regmap,
|
ret = regmap_update_bits(info->regmap,
|
||||||
AXP20X_CHRG_CTRL2,
|
AXP20X_CHRG_CTRL2,
|
||||||
CNTL2_CHG_OUT_TURNON, 1);
|
CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||||
AXP20X_CHRG_CTRL2, ret);
|
AXP20X_CHRG_CTRL2, ret);
|
||||||
|
return 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);
|
|
||||||
|
|
||||||
/* Setup ending condition for charging to be 10% of I(chrg) */
|
/* Setup ending condition for charging to be 10% of I(chrg) */
|
||||||
ret = regmap_update_bits(info->regmap,
|
ret = regmap_update_bits(info->regmap,
|
||||||
AXP20X_CHRG_CTRL1,
|
AXP20X_CHRG_CTRL1,
|
||||||
CHRG_CCCV_ITERM_20P, 0);
|
CHRG_CCCV_ITERM_20P, 0);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||||
AXP20X_CHRG_CTRL1, ret);
|
AXP20X_CHRG_CTRL1, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable OCV-SOC curve calibration */
|
/* Disable OCV-SOC curve calibration */
|
||||||
ret = regmap_update_bits(info->regmap,
|
ret = regmap_update_bits(info->regmap,
|
||||||
AXP20X_CC_CTRL,
|
AXP20X_CC_CTRL,
|
||||||
FG_CNTL_OCV_ADJ_EN, 0);
|
FG_CNTL_OCV_ADJ_EN, 0);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||||
AXP20X_CC_CTRL, ret);
|
AXP20X_CC_CTRL, ret);
|
||||||
|
return ret;
|
||||||
/* Init charging current and voltage */
|
}
|
||||||
info->max_cc = info->pdata->max_cc;
|
|
||||||
info->max_cv = info->pdata->max_cv;
|
|
||||||
|
|
||||||
/* Read current charge voltage and current limit */
|
/* Read current charge voltage and current limit */
|
||||||
ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
|
ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Assume default if cannot read */
|
dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
|
||||||
info->cc = info->pdata->def_cc;
|
AXP20X_CHRG_CTRL1, ret);
|
||||||
info->cv = info->pdata->def_cv;
|
return ret;
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* Program default charging voltage and current */
|
|
||||||
cc = min(info->pdata->def_cc, info->max_cc);
|
|
||||||
cv = min(info->pdata->def_cv, info->max_cv);
|
|
||||||
|
|
||||||
ret = axp288_charger_set_cc(info, cc);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_warn(&info->pdev->dev,
|
|
||||||
"error(%d) in setting CC\n", ret);
|
|
||||||
|
|
||||||
ret = axp288_charger_set_cv(info, cv);
|
|
||||||
if (ret < 0)
|
|
||||||
dev_warn(&info->pdev->dev,
|
|
||||||
"error(%d) in setting CV\n", ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int axp288_charger_probe(struct platform_device *pdev)
|
static int axp288_charger_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret, i, pirq;
|
int ret, i, pirq;
|
||||||
struct axp288_chrg_info *info;
|
struct axp288_chrg_info *info;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct power_supply_config charger_cfg = {};
|
struct power_supply_config charger_cfg = {};
|
||||||
|
|
||||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (!info)
|
if (!info)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -821,15 +797,8 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
info->regmap = axp20x->regmap;
|
info->regmap = axp20x->regmap;
|
||||||
info->regmap_irqc = axp20x->regmap_irqc;
|
info->regmap_irqc = axp20x->regmap_irqc;
|
||||||
info->pdata = pdev->dev.platform_data;
|
info->cable.chg_type = -1;
|
||||||
|
info->is_charger_enabled = -1;
|
||||||
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.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
|
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
|
||||||
if (info->cable.edev == NULL) {
|
if (info->cable.edev == NULL) {
|
||||||
|
@ -838,63 +807,55 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register for extcon notification */
|
info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_DEV_NAME);
|
||||||
INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
|
if (info->otg.cable == NULL) {
|
||||||
info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
|
dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
|
||||||
ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
|
return -EPROBE_DEFER;
|
||||||
&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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
mutex_init(&info->lock);
|
mutex_init(&info->lock);
|
||||||
|
|
||||||
|
ret = charger_init_hw_regs(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* Register with power supply class */
|
/* Register with power supply class */
|
||||||
charger_cfg.drv_data = info;
|
charger_cfg.drv_data = info;
|
||||||
info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
|
info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
|
||||||
&charger_cfg);
|
&charger_cfg);
|
||||||
if (IS_ERR(info->psy_usb)) {
|
if (IS_ERR(info->psy_usb)) {
|
||||||
dev_err(&pdev->dev, "failed to register power supply charger\n");
|
|
||||||
ret = PTR_ERR(info->psy_usb);
|
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 */
|
/* Register for OTG notification */
|
||||||
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
|
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
|
||||||
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
|
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
|
||||||
ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST,
|
ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
|
||||||
&info->otg.id_nb);
|
EXTCON_USB_HOST, &info->otg.id_nb);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_warn(&pdev->dev, "failed to register otg notifier\n");
|
dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
|
||||||
|
return ret;
|
||||||
if (info->otg.cable)
|
}
|
||||||
info->otg.id_short = extcon_get_cable_state_(
|
schedule_work(&info->otg.work);
|
||||||
info->otg.cable, EXTCON_USB_HOST);
|
|
||||||
|
|
||||||
/* Register charger interrupts */
|
/* Register charger interrupts */
|
||||||
for (i = 0; i < CHRG_INTR_END; i++) {
|
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) {
|
if (info->irq[i] < 0) {
|
||||||
dev_warn(&info->pdev->dev,
|
dev_warn(&info->pdev->dev,
|
||||||
"failed to get virtual interrupt=%d\n", pirq);
|
"failed to get virtual interrupt=%d\n", pirq);
|
||||||
ret = info->irq[i];
|
return info->irq[i];
|
||||||
goto intr_reg_failed;
|
|
||||||
}
|
}
|
||||||
ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
|
ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
|
||||||
NULL, axp288_charger_irq_thread_handler,
|
NULL, axp288_charger_irq_thread_handler,
|
||||||
|
@ -912,51 +872,22 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
|
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
|
||||||
info->irq[i]);
|
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;
|
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 = {
|
static struct platform_driver axp288_charger_driver = {
|
||||||
.probe = axp288_charger_probe,
|
.probe = axp288_charger_probe,
|
||||||
.remove = axp288_charger_remove,
|
.id_table = axp288_charger_id_table,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "axp288_charger",
|
.name = "axp288_charger",
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/iio/consumer.h>
|
#include <linux/iio/consumer.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
||||||
#define CHRG_STAT_BAT_VALID (1 << 4)
|
#define CHRG_STAT_BAT_VALID (1 << 4)
|
||||||
|
@ -49,23 +50,6 @@
|
||||||
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
|
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
|
||||||
#define CHRG_CCCV_CHG_EN (1 << 7)
|
#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_STAT (1 << 2)
|
||||||
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
|
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
|
||||||
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
|
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
|
||||||
|
@ -73,17 +57,15 @@
|
||||||
#define FG_CNTL_CC_EN (1 << 6)
|
#define FG_CNTL_CC_EN (1 << 6)
|
||||||
#define FG_CNTL_GAUGE_EN (1 << 7)
|
#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_VALID (1 << 7)
|
||||||
#define FG_REP_CAP_VAL_MASK 0x7F
|
#define FG_REP_CAP_VAL_MASK 0x7F
|
||||||
|
|
||||||
#define FG_DES_CAP1_VALID (1 << 7)
|
#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_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_DES_CC_RES_LSB 1456 /* 1.456mAhr */
|
||||||
|
|
||||||
#define FG_OCV_CAP_VALID (1 << 7)
|
#define FG_OCV_CAP_VALID (1 << 7)
|
||||||
|
@ -104,9 +86,7 @@
|
||||||
|
|
||||||
/* 1.1mV per LSB expressed in uV */
|
/* 1.1mV per LSB expressed in uV */
|
||||||
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
|
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
|
||||||
/* properties converted to tenths of degrees, uV, uA, uW */
|
/* properties converted to uV, uA */
|
||||||
#define PROP_TEMP(a) ((a) * 10)
|
|
||||||
#define UNPROP_TEMP(a) ((a) / 10)
|
|
||||||
#define PROP_VOLT(a) ((a) * 1000)
|
#define PROP_VOLT(a) ((a) * 1000)
|
||||||
#define PROP_CURR(a) ((a) * 1000)
|
#define PROP_CURR(a) ((a) * 1000)
|
||||||
|
|
||||||
|
@ -122,13 +102,13 @@ enum {
|
||||||
|
|
||||||
struct axp288_fg_info {
|
struct axp288_fg_info {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct axp20x_fg_pdata *pdata;
|
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct regmap_irq_chip_data *regmap_irqc;
|
struct regmap_irq_chip_data *regmap_irqc;
|
||||||
int irq[AXP288_FG_INTR_NUM];
|
int irq[AXP288_FG_INTR_NUM];
|
||||||
struct power_supply *bat;
|
struct power_supply *bat;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
int status;
|
int status;
|
||||||
|
int max_volt;
|
||||||
struct delayed_work status_monitor;
|
struct delayed_work status_monitor;
|
||||||
struct dentry *debug_file;
|
struct dentry *debug_file;
|
||||||
};
|
};
|
||||||
|
@ -138,22 +118,14 @@ static enum power_supply_property fuel_gauge_props[] = {
|
||||||
POWER_SUPPLY_PROP_PRESENT,
|
POWER_SUPPLY_PROP_PRESENT,
|
||||||
POWER_SUPPLY_PROP_HEALTH,
|
POWER_SUPPLY_PROP_HEALTH,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||||
POWER_SUPPLY_PROP_CAPACITY,
|
POWER_SUPPLY_PROP_CAPACITY,
|
||||||
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
|
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_TECHNOLOGY,
|
||||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
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)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
|
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -187,6 +161,44 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
|
||||||
return ret;
|
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,
|
static int pmic_read_adc_val(const char *name, int *raw_val,
|
||||||
struct axp288_fg_info *info)
|
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",
|
seq_printf(s, " FG_RDC0[%02x] : %02x\n",
|
||||||
AXP288_FG_RDC0_REG,
|
AXP288_FG_RDC0_REG,
|
||||||
fuel_gauge_reg_readb(info, 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,
|
AXP288_FG_OCVH_REG,
|
||||||
fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));
|
fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
|
||||||
seq_printf(s, " FG_OCVL[%02x] : %02x\n",
|
seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
|
||||||
AXP288_FG_OCVL_REG,
|
|
||||||
fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));
|
|
||||||
seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",
|
|
||||||
AXP288_FG_DES_CAP1_REG,
|
AXP288_FG_DES_CAP1_REG,
|
||||||
fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG));
|
fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
|
||||||
seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",
|
seq_printf(s, " FG_CC_MTR[%02x] : %04x\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",
|
|
||||||
AXP288_FG_CC_MTR1_REG,
|
AXP288_FG_CC_MTR1_REG,
|
||||||
fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG));
|
fuel_gauge_read_15bit_word(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));
|
|
||||||
seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
|
seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
|
||||||
AXP288_FG_OCV_CAP_REG,
|
AXP288_FG_OCV_CAP_REG,
|
||||||
fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
|
fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
|
||||||
|
@ -417,143 +420,27 @@ current_read_fail:
|
||||||
return ret;
|
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)
|
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
|
||||||
{
|
{
|
||||||
int ret, value;
|
int ret;
|
||||||
|
|
||||||
/* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */
|
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
|
||||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);
|
if (ret >= 0)
|
||||||
if (ret < 0)
|
*vocv = VOLTAGE_FROM_ADC(ret);
|
||||||
goto vocv_read_fail;
|
|
||||||
value = ret << 4;
|
|
||||||
|
|
||||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);
|
|
||||||
if (ret < 0)
|
|
||||||
goto vocv_read_fail;
|
|
||||||
value |= (ret & 0xf);
|
|
||||||
|
|
||||||
*vocv = VOLTAGE_FROM_ADC(value);
|
|
||||||
vocv_read_fail:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
|
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
|
||||||
{
|
{
|
||||||
int temp, vocv;
|
int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||||
int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
|
||||||
|
|
||||||
ret = fuel_gauge_get_btemp(info, &temp);
|
|
||||||
if (ret < 0)
|
|
||||||
goto health_read_fail;
|
|
||||||
|
|
||||||
ret = fuel_gauge_get_vocv(info, &vocv);
|
ret = fuel_gauge_get_vocv(info, &vocv);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto health_read_fail;
|
goto health_read_fail;
|
||||||
|
|
||||||
if (vocv > info->pdata->max_volt)
|
if (vocv > info->max_volt)
|
||||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
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
|
else
|
||||||
health = POWER_SUPPLY_HEALTH_GOOD;
|
health = POWER_SUPPLY_HEALTH_GOOD;
|
||||||
|
|
||||||
|
@ -561,28 +448,6 @@ health_read_fail:
|
||||||
return health;
|
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,
|
static int fuel_gauge_get_property(struct power_supply *ps,
|
||||||
enum power_supply_property prop,
|
enum power_supply_property prop,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
|
@ -643,58 +508,25 @@ static int fuel_gauge_get_property(struct power_supply *ps,
|
||||||
goto fuel_gauge_read_err;
|
goto fuel_gauge_read_err;
|
||||||
val->intval = (ret & 0x0f);
|
val->intval = (ret & 0x0f);
|
||||||
break;
|
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:
|
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
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)
|
if (ret < 0)
|
||||||
goto fuel_gauge_read_err;
|
goto fuel_gauge_read_err;
|
||||||
|
|
||||||
value = (ret & FG_CC_MTR1_VAL_MASK) << 8;
|
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||||
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;
|
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
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)
|
if (ret < 0)
|
||||||
goto fuel_gauge_read_err;
|
goto fuel_gauge_read_err;
|
||||||
|
|
||||||
value = (ret & FG_DES_CAP1_VAL_MASK) << 8;
|
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||||
val->intval = PROP_VOLT(info->pdata->max_volt);
|
val->intval = PROP_VOLT(info->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;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mutex_unlock(&info->lock);
|
mutex_unlock(&info->lock);
|
||||||
|
@ -718,35 +550,6 @@ static int fuel_gauge_set_property(struct power_supply *ps,
|
||||||
|
|
||||||
mutex_lock(&info->lock);
|
mutex_lock(&info->lock);
|
||||||
switch (prop) {
|
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:
|
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||||
if ((val->intval < 0) || (val->intval > 15)) {
|
if ((val->intval < 0) || (val->intval > 15)) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -774,11 +577,6 @@ static int fuel_gauge_property_is_writeable(struct power_supply *psy,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (psp) {
|
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:
|
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||||
ret = 1;
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -863,158 +661,6 @@ static const struct power_supply_desc fuel_gauge_desc = {
|
||||||
.external_power_changed = fuel_gauge_external_power_changed,
|
.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)
|
static void fuel_gauge_init_irq(struct axp288_fg_info *info)
|
||||||
{
|
{
|
||||||
int ret, i, pirq;
|
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)
|
static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -1090,15 +713,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||||
info->regmap = axp20x->regmap;
|
info->regmap = axp20x->regmap;
|
||||||
info->regmap_irqc = axp20x->regmap_irqc;
|
info->regmap_irqc = axp20x->regmap_irqc;
|
||||||
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
|
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||||
info->pdata = pdev->dev.platform_data;
|
|
||||||
if (!info->pdata)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
|
|
||||||
mutex_init(&info->lock);
|
mutex_init(&info->lock);
|
||||||
INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
|
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;
|
psy_cfg.drv_data = info;
|
||||||
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
|
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
|
||||||
if (IS_ERR(info->bat)) {
|
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_create_debugfs(info);
|
||||||
fuel_gauge_init_config_regs(info);
|
|
||||||
fuel_gauge_init_irq(info);
|
fuel_gauge_init_irq(info);
|
||||||
fuel_gauge_init_hw_regs(info);
|
|
||||||
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
|
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct platform_device_id axp288_fg_id_table[] = {
|
static const struct platform_device_id axp288_fg_id_table[] = {
|
||||||
|
|
|
@ -1569,6 +1569,11 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||||
acpi_id =
|
acpi_id =
|
||||||
acpi_match_device(client->dev.driver->acpi_match_table,
|
acpi_match_device(client->dev.driver->acpi_match_table,
|
||||||
&client->dev);
|
&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);
|
name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
|
||||||
}
|
}
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
|
@ -144,10 +144,7 @@
|
||||||
* so the first read after a fault returns the latched value and subsequent
|
* 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
|
* 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
|
* 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
|
* it in the appropriate health/status routine.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
struct bq24190_dev_info {
|
struct bq24190_dev_info {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
@ -159,10 +156,6 @@ struct bq24190_dev_info {
|
||||||
unsigned int gpio_int;
|
unsigned int gpio_int;
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
struct mutex f_reg_lock;
|
struct mutex f_reg_lock;
|
||||||
bool first_time;
|
|
||||||
bool charger_health_valid;
|
|
||||||
bool battery_health_valid;
|
|
||||||
bool battery_status_valid;
|
|
||||||
u8 f_reg;
|
u8 f_reg;
|
||||||
u8 ss_reg;
|
u8 ss_reg;
|
||||||
u8 watchdog;
|
u8 watchdog;
|
||||||
|
@ -199,7 +192,7 @@ static const int bq24190_cvc_vreg_values[] = {
|
||||||
4400000
|
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[] = {
|
static const int bq24190_ictrc_treg_values[] = {
|
||||||
600, 800, 1000, 1200
|
600, 800, 1000, 1200
|
||||||
};
|
};
|
||||||
|
@ -636,21 +629,11 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
u8 v;
|
u8 v;
|
||||||
int health, ret;
|
int health;
|
||||||
|
|
||||||
mutex_lock(&bdi->f_reg_lock);
|
mutex_lock(&bdi->f_reg_lock);
|
||||||
|
v = bdi->f_reg;
|
||||||
if (bdi->charger_health_valid) {
|
mutex_unlock(&bdi->f_reg_lock);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
|
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;
|
int status, ret;
|
||||||
|
|
||||||
mutex_lock(&bdi->f_reg_lock);
|
mutex_lock(&bdi->f_reg_lock);
|
||||||
|
chrg_fault = bdi->f_reg;
|
||||||
if (bdi->battery_status_valid) {
|
mutex_unlock(&bdi->f_reg_lock);
|
||||||
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 &= BQ24190_REG_F_CHRG_FAULT_MASK;
|
chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
|
||||||
chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
|
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)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
u8 v;
|
u8 v;
|
||||||
int health, ret;
|
int health;
|
||||||
|
|
||||||
mutex_lock(&bdi->f_reg_lock);
|
mutex_lock(&bdi->f_reg_lock);
|
||||||
|
v = bdi->f_reg;
|
||||||
if (bdi->battery_health_valid) {
|
mutex_unlock(&bdi->f_reg_lock);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
|
if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
|
||||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
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)
|
static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct bq24190_dev_info *bdi = 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;
|
u8 ss_reg = 0, f_reg = 0;
|
||||||
int ret;
|
int i, ret;
|
||||||
|
|
||||||
pm_runtime_get_sync(bdi->dev);
|
pm_runtime_get_sync(bdi->dev);
|
||||||
|
|
||||||
|
@ -1209,6 +1175,32 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||||
goto out;
|
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) {
|
if (ss_reg != bdi->ss_reg) {
|
||||||
/*
|
/*
|
||||||
* The device is in host mode so when PG_STAT goes from 1->0
|
* 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);
|
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;
|
bdi->ss_reg = ss_reg;
|
||||||
alert_userspace = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&bdi->f_reg_lock);
|
if (alert_charger)
|
||||||
|
power_supply_changed(bdi->charger);
|
||||||
ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
|
if (alert_battery)
|
||||||
if (ret < 0) {
|
power_supply_changed(bdi->battery);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
pm_runtime_put_sync(bdi->dev);
|
pm_runtime_put_sync(bdi->dev);
|
||||||
|
@ -1300,6 +1262,10 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = bq24190_set_mode_host(bdi);
|
ret = bq24190_set_mode_host(bdi);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
|
||||||
out:
|
out:
|
||||||
pm_runtime_put_sync(bdi->dev);
|
pm_runtime_put_sync(bdi->dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1375,10 +1341,8 @@ static int bq24190_probe(struct i2c_client *client,
|
||||||
bdi->model = id->driver_data;
|
bdi->model = id->driver_data;
|
||||||
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
|
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
|
||||||
mutex_init(&bdi->f_reg_lock);
|
mutex_init(&bdi->f_reg_lock);
|
||||||
bdi->first_time = true;
|
bdi->f_reg = 0;
|
||||||
bdi->charger_health_valid = false;
|
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
|
||||||
bdi->battery_health_valid = false;
|
|
||||||
bdi->battery_status_valid = false;
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, bdi);
|
i2c_set_clientdata(client, bdi);
|
||||||
|
|
||||||
|
@ -1392,22 +1356,13 @@ static int bq24190_probe(struct i2c_client *client,
|
||||||
return -EINVAL;
|
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_enable(dev);
|
||||||
pm_runtime_resume(dev);
|
pm_runtime_resume(dev);
|
||||||
|
|
||||||
ret = bq24190_hw_init(bdi);
|
ret = bq24190_hw_init(bdi);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Hardware init failed\n");
|
dev_err(dev, "Hardware init failed\n");
|
||||||
goto out2;
|
goto out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
charger_cfg.drv_data = bdi;
|
charger_cfg.drv_data = bdi;
|
||||||
|
@ -1418,7 +1373,7 @@ static int bq24190_probe(struct i2c_client *client,
|
||||||
if (IS_ERR(bdi->charger)) {
|
if (IS_ERR(bdi->charger)) {
|
||||||
dev_err(dev, "Can't register charger\n");
|
dev_err(dev, "Can't register charger\n");
|
||||||
ret = PTR_ERR(bdi->charger);
|
ret = PTR_ERR(bdi->charger);
|
||||||
goto out2;
|
goto out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
battery_cfg.drv_data = bdi;
|
battery_cfg.drv_data = bdi;
|
||||||
|
@ -1427,27 +1382,39 @@ static int bq24190_probe(struct i2c_client *client,
|
||||||
if (IS_ERR(bdi->battery)) {
|
if (IS_ERR(bdi->battery)) {
|
||||||
dev_err(dev, "Can't register battery\n");
|
dev_err(dev, "Can't register battery\n");
|
||||||
ret = PTR_ERR(bdi->battery);
|
ret = PTR_ERR(bdi->battery);
|
||||||
goto out3;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bq24190_sysfs_create_group(bdi);
|
ret = bq24190_sysfs_create_group(bdi);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Can't create sysfs entries\n");
|
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;
|
goto out4;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out4:
|
out4:
|
||||||
power_supply_unregister(bdi->battery);
|
bq24190_sysfs_remove_group(bdi);
|
||||||
|
|
||||||
out3:
|
out3:
|
||||||
power_supply_unregister(bdi->charger);
|
power_supply_unregister(bdi->battery);
|
||||||
|
|
||||||
out2:
|
out2:
|
||||||
pm_runtime_disable(dev);
|
power_supply_unregister(bdi->charger);
|
||||||
|
|
||||||
out1:
|
out1:
|
||||||
|
pm_runtime_disable(dev);
|
||||||
if (bdi->gpio_int)
|
if (bdi->gpio_int)
|
||||||
gpio_free(bdi->gpio_int);
|
gpio_free(bdi->gpio_int);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1488,12 +1455,13 @@ static int bq24190_pm_resume(struct device *dev)
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
||||||
|
|
||||||
bdi->charger_health_valid = false;
|
bdi->f_reg = 0;
|
||||||
bdi->battery_health_valid = false;
|
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
|
||||||
bdi->battery_status_valid = false;
|
|
||||||
|
|
||||||
pm_runtime_get_sync(bdi->dev);
|
pm_runtime_get_sync(bdi->dev);
|
||||||
bq24190_register_reset(bdi);
|
bq24190_register_reset(bdi);
|
||||||
|
bq24190_set_mode_host(bdi);
|
||||||
|
bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
|
||||||
pm_runtime_put_sync(bdi->dev);
|
pm_runtime_put_sync(bdi->dev);
|
||||||
|
|
||||||
/* Things may have changed while suspended so alert upper layer */
|
/* Things may have changed while suspended so alert upper layer */
|
||||||
|
|
|
@ -50,6 +50,8 @@ struct bq24735 {
|
||||||
struct bq24735_platform *pdata;
|
struct bq24735_platform *pdata;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct gpio_desc *status_gpio;
|
struct gpio_desc *status_gpio;
|
||||||
|
struct delayed_work poll;
|
||||||
|
u32 poll_interval;
|
||||||
bool charging;
|
bool charging;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,26 +107,6 @@ static int bq24735_update_word(struct i2c_client *client, u8 reg,
|
||||||
return bq24735_write_word(client, reg, tmp);
|
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)
|
static int bq24735_config_charger(struct bq24735 *charger)
|
||||||
{
|
{
|
||||||
struct bq24735_platform *pdata = charger->pdata;
|
struct bq24735_platform *pdata = charger->pdata;
|
||||||
|
@ -176,6 +158,31 @@ static int bq24735_config_charger(struct bq24735 *charger)
|
||||||
return 0;
|
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)
|
static bool bq24735_charger_is_present(struct bq24735 *charger)
|
||||||
{
|
{
|
||||||
if (charger->status_gpio) {
|
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);
|
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
|
||||||
if (ac < 0) {
|
if (ac < 0) {
|
||||||
dev_err(&charger->client->dev,
|
dev_dbg(&charger->client->dev,
|
||||||
"Failed to read charger options : %d\n",
|
"Failed to read charger options : %d\n",
|
||||||
ac);
|
ac);
|
||||||
return false;
|
return false;
|
||||||
|
@ -210,11 +217,8 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)
|
||||||
return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
|
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);
|
mutex_lock(&charger->lock);
|
||||||
|
|
||||||
if (charger->charging && bq24735_charger_is_present(charger))
|
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);
|
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;
|
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,
|
static int bq24735_charger_get_property(struct power_supply *psy,
|
||||||
enum power_supply_property psp,
|
enum power_supply_property psp,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
|
@ -276,7 +298,6 @@ static int bq24735_charger_set_property(struct power_supply *psy,
|
||||||
mutex_unlock(&charger->lock);
|
mutex_unlock(&charger->lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
bq24735_config_charger(charger);
|
|
||||||
break;
|
break;
|
||||||
case POWER_SUPPLY_STATUS_DISCHARGING:
|
case POWER_SUPPLY_STATUS_DISCHARGING:
|
||||||
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||||||
|
@ -395,7 +416,7 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||||
return ret;
|
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);
|
ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
|
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);
|
"device id mismatch. 0x000b != 0x%04x\n", ret);
|
||||||
return -ENODEV;
|
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);
|
ret = bq24735_enable_charging(charger);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "Failed to enable charging\n");
|
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);
|
client->irq, ret);
|
||||||
return 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;
|
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[] = {
|
static const struct i2c_device_id bq24735_charger_id[] = {
|
||||||
{ "bq24735-charger", 0 },
|
{ "bq24735-charger", 0 },
|
||||||
{}
|
{}
|
||||||
|
@ -479,6 +512,7 @@ static struct i2c_driver bq24735_charger_driver = {
|
||||||
.of_match_table = bq24735_match_ids,
|
.of_match_table = bq24735_match_ids,
|
||||||
},
|
},
|
||||||
.probe = bq24735_charger_probe,
|
.probe = bq24735_charger_probe,
|
||||||
|
.remove = bq24735_charger_remove,
|
||||||
.id_table = bq24735_charger_id,
|
.id_table = bq24735_charger_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,14 @@
|
||||||
* http://www.ti.com/product/bq27010
|
* http://www.ti.com/product/bq27010
|
||||||
* http://www.ti.com/product/bq27210
|
* http://www.ti.com/product/bq27210
|
||||||
* http://www.ti.com/product/bq27500
|
* 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/bq27510-g3
|
||||||
* http://www.ti.com/product/bq27520-g4
|
* 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/bq27530-g1
|
||||||
* http://www.ti.com/product/bq27531-g1
|
* http://www.ti.com/product/bq27531-g1
|
||||||
* http://www.ti.com/product/bq27541-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_DCAP] = 0x76,
|
||||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||||
},
|
},
|
||||||
[BQ27500] = {
|
[BQ2750X] = {
|
||||||
[BQ27XXX_REG_CTRL] = 0x00,
|
[BQ27XXX_REG_CTRL] = 0x00,
|
||||||
[BQ27XXX_REG_TEMP] = 0x06,
|
[BQ27XXX_REG_TEMP] = 0x06,
|
||||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||||
|
@ -164,7 +170,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
||||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||||
},
|
},
|
||||||
[BQ27510] = {
|
[BQ2751X] = {
|
||||||
[BQ27XXX_REG_CTRL] = 0x00,
|
[BQ27XXX_REG_CTRL] = 0x00,
|
||||||
[BQ27XXX_REG_TEMP] = 0x06,
|
[BQ27XXX_REG_TEMP] = 0x06,
|
||||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||||
|
@ -183,6 +189,158 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
||||||
[BQ27XXX_REG_DCAP] = 0x2e,
|
[BQ27XXX_REG_DCAP] = 0x2e,
|
||||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
[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,
|
||||||
|
[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,
|
||||||
|
},
|
||||||
|
[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] = {
|
[BQ27530] = {
|
||||||
[BQ27XXX_REG_CTRL] = 0x00,
|
[BQ27XXX_REG_CTRL] = 0x00,
|
||||||
[BQ27XXX_REG_TEMP] = 0x06,
|
[BQ27XXX_REG_TEMP] = 0x06,
|
||||||
|
@ -303,7 +461,7 @@ static enum power_supply_property bq27010_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum power_supply_property bq27500_battery_props[] = {
|
static enum power_supply_property bq2750x_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_STATUS,
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
POWER_SUPPLY_PROP_PRESENT,
|
POWER_SUPPLY_PROP_PRESENT,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
|
@ -321,7 +479,7 @@ static enum power_supply_property bq27500_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum power_supply_property bq27510_battery_props[] = {
|
static enum power_supply_property bq2751x_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_STATUS,
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
POWER_SUPPLY_PROP_PRESENT,
|
POWER_SUPPLY_PROP_PRESENT,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
|
@ -339,6 +497,165 @@ static enum power_supply_property bq27510_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static enum power_supply_property bq27500_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 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,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||||
|
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||||
|
POWER_SUPPLY_PROP_HEALTH,
|
||||||
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
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 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,
|
||||||
|
};
|
||||||
|
|
||||||
static enum power_supply_property bq27530_battery_props[] = {
|
static enum power_supply_property bq27530_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_STATUS,
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
POWER_SUPPLY_PROP_PRESENT,
|
POWER_SUPPLY_PROP_PRESENT,
|
||||||
|
@ -421,8 +738,16 @@ static struct {
|
||||||
} bq27xxx_battery_props[] = {
|
} bq27xxx_battery_props[] = {
|
||||||
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
|
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
|
||||||
BQ27XXX_PROP(BQ27010, bq27010_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(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(BQ27530, bq27530_battery_props),
|
||||||
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
|
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
|
||||||
BQ27XXX_PROP(BQ27545, bq27545_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)
|
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
|
||||||
{
|
{
|
||||||
if (di->chip == BQ27500 || di->chip == BQ27510 ||
|
switch (di->chip) {
|
||||||
di->chip == BQ27541 || di->chip == BQ27545)
|
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);
|
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 flags & BQ27XXX_FLAG_OT;
|
||||||
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -148,9 +148,17 @@ static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
|
||||||
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
||||||
{ "bq27200", BQ27000 },
|
{ "bq27200", BQ27000 },
|
||||||
{ "bq27210", BQ27010 },
|
{ "bq27210", BQ27010 },
|
||||||
{ "bq27500", BQ27500 },
|
{ "bq27500", BQ2750X },
|
||||||
{ "bq27510", BQ27510 },
|
{ "bq27510", BQ2751X },
|
||||||
{ "bq27520", BQ27510 },
|
{ "bq27520", BQ2751X },
|
||||||
|
{ "bq27500-1", BQ27500 },
|
||||||
|
{ "bq27510g1", BQ27510G1 },
|
||||||
|
{ "bq27510g2", BQ27510G2 },
|
||||||
|
{ "bq27510g3", BQ27510G3 },
|
||||||
|
{ "bq27520g1", BQ27520G1 },
|
||||||
|
{ "bq27520g2", BQ27520G2 },
|
||||||
|
{ "bq27520g3", BQ27520G3 },
|
||||||
|
{ "bq27520g4", BQ27520G4 },
|
||||||
{ "bq27530", BQ27530 },
|
{ "bq27530", BQ27530 },
|
||||||
{ "bq27531", BQ27530 },
|
{ "bq27531", BQ27530 },
|
||||||
{ "bq27541", BQ27541 },
|
{ "bq27541", BQ27541 },
|
||||||
|
@ -173,6 +181,14 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
|
||||||
{ .compatible = "ti,bq27500" },
|
{ .compatible = "ti,bq27500" },
|
||||||
{ .compatible = "ti,bq27510" },
|
{ .compatible = "ti,bq27510" },
|
||||||
{ .compatible = "ti,bq27520" },
|
{ .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,bq27530" },
|
||||||
{ .compatible = "ti,bq27531" },
|
{ .compatible = "ti,bq27531" },
|
||||||
{ .compatible = "ti,bq27541" },
|
{ .compatible = "ti,bq27541" },
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h> /* For legacy platform data */
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
|
|
||||||
#include <linux/power/gpio-charger.h>
|
#include <linux/power/gpio-charger.h>
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ struct gpio_charger {
|
||||||
|
|
||||||
struct power_supply *charger;
|
struct power_supply *charger;
|
||||||
struct power_supply_desc charger_desc;
|
struct power_supply_desc charger_desc;
|
||||||
|
struct gpio_desc *gpiod;
|
||||||
|
bool legacy_gpio_requested;
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t gpio_charger_irq(int irq, void *devid)
|
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) {
|
switch (psp) {
|
||||||
case POWER_SUPPLY_PROP_ONLINE:
|
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;
|
val->intval ^= pdata->gpio_active_low;
|
||||||
break;
|
break;
|
||||||
default:
|
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 device_node *np = dev->of_node;
|
||||||
struct gpio_charger_platform_data *pdata;
|
struct gpio_charger_platform_data *pdata;
|
||||||
const char *chargetype;
|
const char *chargetype;
|
||||||
enum of_gpio_flags flags;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
|
@ -89,16 +91,6 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
pdata->name = np->name;
|
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;
|
pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||||
ret = of_property_read_string(np, "charger-type", &chargetype);
|
ret = of_property_read_string(np, "charger-type", &chargetype);
|
||||||
if (ret >= 0) {
|
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),
|
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!gpio_charger) {
|
if (!gpio_charger) {
|
||||||
|
@ -156,6 +143,45 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
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 = &gpio_charger->charger_desc;
|
||||||
|
|
||||||
charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
|
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.of_node = pdev->dev.of_node;
|
||||||
psy_cfg.drv_data = gpio_charger;
|
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->pdata = pdata;
|
||||||
|
|
||||||
gpio_charger->charger = power_supply_register(&pdev->dev,
|
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;
|
goto err_gpio_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = gpio_to_irq(pdata->gpio);
|
irq = gpiod_to_irq(gpio_charger->gpiod);
|
||||||
if (irq > 0) {
|
if (irq > 0) {
|
||||||
ret = request_any_context_irq(irq, gpio_charger_irq,
|
ret = request_any_context_irq(irq, gpio_charger_irq,
|
||||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
|
@ -209,8 +224,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_gpio_free:
|
err_gpio_free:
|
||||||
gpio_free(pdata->gpio);
|
if (gpio_charger->legacy_gpio_requested)
|
||||||
err_free:
|
gpio_free(pdata->gpio);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +238,8 @@ static int gpio_charger_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
power_supply_unregister(gpio_charger->charger);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
|
|
@ -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");
|
|
@ -148,10 +148,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
|
charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (charger == NULL) {
|
if (!charger)
|
||||||
dev_err(&pdev->dev, "Cannot allocate memory.\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, charger);
|
platform_set_drvdata(pdev, charger);
|
||||||
|
|
||||||
|
@ -161,7 +159,7 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
psy_cfg.drv_data = charger;
|
psy_cfg.drv_data = charger;
|
||||||
|
|
||||||
charger->battery = power_supply_register(&pdev->dev,
|
charger->battery = devm_power_supply_register(&pdev->dev,
|
||||||
&max8997_battery_desc,
|
&max8997_battery_desc,
|
||||||
&psy_cfg);
|
&psy_cfg);
|
||||||
if (IS_ERR(charger->battery)) {
|
if (IS_ERR(charger->battery)) {
|
||||||
|
@ -172,14 +170,6 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||||
return 0;
|
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[] = {
|
static const struct platform_device_id max8997_battery_id[] = {
|
||||||
{ "max8997-battery", 0 },
|
{ "max8997-battery", 0 },
|
||||||
{ }
|
{ }
|
||||||
|
@ -191,7 +181,6 @@ static struct platform_driver max8997_battery_driver = {
|
||||||
.name = "max8997-battery",
|
.name = "max8997-battery",
|
||||||
},
|
},
|
||||||
.probe = max8997_battery_probe,
|
.probe = max8997_battery_probe,
|
||||||
.remove = max8997_battery_remove,
|
|
||||||
.id_table = max8997_battery_id,
|
.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 power_supply_config psy_cfg = {};
|
||||||
struct pcf50633_mbc *mbc;
|
struct pcf50633_mbc *mbc;
|
||||||
int ret;
|
|
||||||
int i;
|
int i;
|
||||||
u8 mbcs1;
|
u8 mbcs1;
|
||||||
|
|
||||||
|
@ -419,8 +418,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
|
||||||
&psy_cfg);
|
&psy_cfg);
|
||||||
if (IS_ERR(mbc->adapter)) {
|
if (IS_ERR(mbc->adapter)) {
|
||||||
dev_err(mbc->pcf->dev, "failed to register adapter\n");
|
dev_err(mbc->pcf->dev, "failed to register adapter\n");
|
||||||
ret = PTR_ERR(mbc->adapter);
|
return PTR_ERR(mbc->adapter);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
|
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)) {
|
if (IS_ERR(mbc->usb)) {
|
||||||
dev_err(mbc->pcf->dev, "failed to register usb\n");
|
dev_err(mbc->pcf->dev, "failed to register usb\n");
|
||||||
power_supply_unregister(mbc->adapter);
|
power_supply_unregister(mbc->adapter);
|
||||||
ret = PTR_ERR(mbc->usb);
|
return PTR_ERR(mbc->usb);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
|
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");
|
dev_err(mbc->pcf->dev, "failed to register ac\n");
|
||||||
power_supply_unregister(mbc->adapter);
|
power_supply_unregister(mbc->adapter);
|
||||||
power_supply_unregister(mbc->usb);
|
power_supply_unregister(mbc->usb);
|
||||||
ret = PTR_ERR(mbc->ac);
|
return PTR_ERR(mbc->ac);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
|
if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))
|
||||||
if (ret)
|
|
||||||
dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
|
dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
|
||||||
|
|
||||||
mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
|
mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/extcon.h>
|
#include <linux/extcon.h>
|
||||||
|
#include <linux/regulator/driver.h>
|
||||||
|
|
||||||
#define SMBB_CHG_VMAX 0x040
|
#define SMBB_CHG_VMAX 0x040
|
||||||
#define SMBB_CHG_VSAFE 0x041
|
#define SMBB_CHG_VSAFE 0x041
|
||||||
|
@ -72,6 +73,8 @@
|
||||||
#define BTC_CTRL_HOT_EXT_N BIT(0)
|
#define BTC_CTRL_HOT_EXT_N BIT(0)
|
||||||
|
|
||||||
#define SMBB_USB_IMAX 0x344
|
#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 SMBB_USB_ENUM_TIMER_STOP 0x34e
|
||||||
#define ENUM_TIMER_STOP BIT(0)
|
#define ENUM_TIMER_STOP BIT(0)
|
||||||
#define SMBB_USB_SEC_ACCESS 0x3d0
|
#define SMBB_USB_SEC_ACCESS 0x3d0
|
||||||
|
@ -125,6 +128,9 @@ struct smbb_charger {
|
||||||
struct power_supply *dc_psy;
|
struct power_supply *dc_psy;
|
||||||
struct power_supply *bat_psy;
|
struct power_supply *bat_psy;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
struct regulator_desc otg_rdesc;
|
||||||
|
struct regulator_dev *otg_reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int smbb_usb_extcon_cable[] = {
|
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;
|
struct smbb_charger *chg = _data;
|
||||||
|
|
||||||
smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
|
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);
|
chg->status & STATUS_USBIN_VALID);
|
||||||
power_supply_changed(chg->usb_psy);
|
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,
|
.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)
|
static int smbb_charger_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct power_supply_config bat_cfg = {};
|
struct power_supply_config bat_cfg = {};
|
||||||
struct power_supply_config usb_cfg = {};
|
struct power_supply_config usb_cfg = {};
|
||||||
struct power_supply_config dc_cfg = {};
|
struct power_supply_config dc_cfg = {};
|
||||||
struct smbb_charger *chg;
|
struct smbb_charger *chg;
|
||||||
|
struct regulator_config config = { };
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
|
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,
|
chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
|
||||||
"qcom,jeita-extended-temp-range");
|
"qcom,jeita-extended-temp-range");
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
@ -35,22 +35,22 @@
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/tps65217.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)
|
#define POLL_INTERVAL (HZ * 2)
|
||||||
|
|
||||||
struct tps65217_charger {
|
struct tps65217_charger {
|
||||||
struct tps65217 *tps;
|
struct tps65217 *tps;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct power_supply *ac;
|
struct power_supply *psy;
|
||||||
|
|
||||||
int ac_online;
|
int online;
|
||||||
int prev_ac_online;
|
int prev_online;
|
||||||
|
|
||||||
struct task_struct *poll_task;
|
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,
|
POWER_SUPPLY_PROP_ONLINE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* charger already enabled */
|
/* charger already enabled */
|
||||||
if (charger->ac_online)
|
if (charger->online)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
dev_dbg(charger->dev, "%s: enable charging\n", __func__);
|
dev_dbg(charger->dev, "%s: enable charging\n", __func__);
|
||||||
|
@ -110,19 +110,19 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
charger->ac_online = 1;
|
charger->online = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tps65217_ac_get_property(struct power_supply *psy,
|
static int tps65217_charger_get_property(struct power_supply *psy,
|
||||||
enum power_supply_property psp,
|
enum power_supply_property psp,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
struct tps65217_charger *charger = power_supply_get_drvdata(psy);
|
struct tps65217_charger *charger = power_supply_get_drvdata(psy);
|
||||||
|
|
||||||
if (psp == POWER_SUPPLY_PROP_ONLINE) {
|
if (psp == POWER_SUPPLY_PROP_ONLINE) {
|
||||||
val->intval = charger->ac_online;
|
val->intval = charger->online;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -133,7 +133,7 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)
|
||||||
int ret, val;
|
int ret, val;
|
||||||
struct tps65217_charger *charger = dev;
|
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);
|
ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
|
||||||
if (ret < 0) {
|
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);
|
dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
|
||||||
|
|
||||||
/* check for AC status bit */
|
/* check for charger status bit */
|
||||||
if (val & TPS65217_STATUS_ACPWR) {
|
if (val & CHARGER_STATUS_PRESENT) {
|
||||||
ret = tps65217_enable_charging(charger);
|
ret = tps65217_enable_charging(charger);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(charger->dev,
|
dev_err(charger->dev,
|
||||||
|
@ -153,11 +153,11 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
charger->ac_online = 0;
|
charger->online = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charger->prev_ac_online != charger->ac_online)
|
if (charger->prev_online != charger->online)
|
||||||
power_supply_changed(charger->ac);
|
power_supply_changed(charger->psy);
|
||||||
|
|
||||||
ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
|
ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -188,11 +188,11 @@ static int tps65217_charger_poll_task(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct power_supply_desc tps65217_charger_desc = {
|
static const struct power_supply_desc tps65217_charger_desc = {
|
||||||
.name = "tps65217-ac",
|
.name = "tps65217-charger",
|
||||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||||
.get_property = tps65217_ac_get_property,
|
.get_property = tps65217_charger_get_property,
|
||||||
.properties = tps65217_ac_props,
|
.properties = tps65217_charger_props,
|
||||||
.num_properties = ARRAY_SIZE(tps65217_ac_props),
|
.num_properties = ARRAY_SIZE(tps65217_charger_props),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tps65217_charger_probe(struct platform_device *pdev)
|
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 *tps = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct tps65217_charger *charger;
|
struct tps65217_charger *charger;
|
||||||
struct power_supply_config cfg = {};
|
struct power_supply_config cfg = {};
|
||||||
int irq;
|
struct task_struct *poll_task;
|
||||||
|
int irq[NUM_CHARGER_IRQS];
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s\n", __func__);
|
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.of_node = pdev->dev.of_node;
|
||||||
cfg.drv_data = charger;
|
cfg.drv_data = charger;
|
||||||
|
|
||||||
charger->ac = devm_power_supply_register(&pdev->dev,
|
charger->psy = devm_power_supply_register(&pdev->dev,
|
||||||
&tps65217_charger_desc,
|
&tps65217_charger_desc,
|
||||||
&cfg);
|
&cfg);
|
||||||
if (IS_ERR(charger->ac)) {
|
if (IS_ERR(charger->psy)) {
|
||||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
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");
|
irq[0] = platform_get_irq_byname(pdev, "USB");
|
||||||
if (irq < 0)
|
irq[1] = platform_get_irq_byname(pdev, "AC");
|
||||||
irq = -ENXIO;
|
|
||||||
charger->irq = irq;
|
|
||||||
|
|
||||||
ret = tps65217_config_charger(charger);
|
ret = tps65217_config_charger(charger);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -235,29 +235,36 @@ static int tps65217_charger_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irq != -ENXIO) {
|
/* Create a polling thread if an interrupt is invalid */
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
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,
|
tps65217_charger_irq,
|
||||||
0, "tps65217-charger",
|
0, "tps65217-charger",
|
||||||
charger);
|
charger);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(charger->dev,
|
dev_err(charger->dev,
|
||||||
"Unable to register irq %d err %d\n", irq,
|
"Unable to register irq %d err %d\n", irq[i],
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check current state */
|
/* Check current state */
|
||||||
tps65217_charger_irq(irq, charger);
|
tps65217_charger_irq(-1, 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -267,7 +274,7 @@ static int tps65217_charger_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct tps65217_charger *charger = platform_get_drvdata(pdev);
|
struct tps65217_charger *charger = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (charger->irq == -ENXIO)
|
if (charger->poll_task)
|
||||||
kthread_stop(charger->poll_task);
|
kthread_stop(charger->poll_task);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -175,11 +175,6 @@ static int wm97xx_bat_probe(struct platform_device *dev)
|
||||||
if (dev->id != -1)
|
if (dev->id != -1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!pdata) {
|
|
||||||
dev_err(&dev->dev, "No platform_data supplied\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->charge_gpio)) {
|
if (gpio_is_valid(pdata->charge_gpio)) {
|
||||||
ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
|
ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
enum {
|
enum axp20x_variants {
|
||||||
AXP152_ID = 0,
|
AXP152_ID = 0,
|
||||||
AXP202_ID,
|
AXP202_ID,
|
||||||
AXP209_ID,
|
AXP209_ID,
|
||||||
|
@ -532,35 +532,6 @@ struct axp20x_dev {
|
||||||
const struct regmap_irq_chip *regmap_irq_chip;
|
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 {
|
struct axp288_extcon_pdata {
|
||||||
/* GPIO pin control to switch D+/D- lines b/w PMIC and SOC */
|
/* GPIO pin control to switch D+/D- lines b/w PMIC and SOC */
|
||||||
struct gpio_desc *gpio_mux_cntl;
|
struct gpio_desc *gpio_mux_cntl;
|
||||||
|
|
|
@ -4,8 +4,16 @@
|
||||||
enum bq27xxx_chip {
|
enum bq27xxx_chip {
|
||||||
BQ27000 = 1, /* bq27000, bq27200 */
|
BQ27000 = 1, /* bq27000, bq27200 */
|
||||||
BQ27010, /* bq27010, bq27210 */
|
BQ27010, /* bq27010, bq27210 */
|
||||||
BQ27500, /* bq27500 */
|
BQ2750X, /* bq27500 deprecated alias */
|
||||||
BQ27510, /* bq27510, bq27520 */
|
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 */
|
BQ27530, /* bq27530, bq27531 */
|
||||||
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
|
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
|
||||||
BQ27545, /* bq27545 */
|
BQ27545, /* bq27545 */
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
#define AT91_DDRSDRC_LPCB_POWER_DOWN 2
|
#define AT91_DDRSDRC_LPCB_POWER_DOWN 2
|
||||||
#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3
|
#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3
|
||||||
#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */
|
#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_PASR (7 << 4) /* Partial Array Self Refresh */
|
||||||
#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */
|
#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */
|
||||||
#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */
|
#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */
|
||||||
|
@ -96,7 +97,9 @@
|
||||||
#define AT91_DDRSDRC_MD_SDR 0
|
#define AT91_DDRSDRC_MD_SDR 0
|
||||||
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
|
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
|
||||||
#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3
|
#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_DDR2 6 /* [SAM9 Only] */
|
||||||
|
#define AT91_DDRSDRC_MD_LPDDR2 7
|
||||||
#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */
|
#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */
|
||||||
#define AT91_DDRSDRC_DBW_32BITS (0 << 4)
|
#define AT91_DDRSDRC_DBW_32BITS (0 << 4)
|
||||||
#define AT91_DDRSDRC_DBW_16BITS (1 << 4)
|
#define AT91_DDRSDRC_DBW_16BITS (1 << 4)
|
||||||
|
|
Loading…
Reference in New Issue