power supply and reset changes for the v6.3 series

- power-supply core: remove faulty cooling logic
 - convert all sysfs show() handlers from *printf() use sysfs_emit()
 - bq25890: add dual-charger support required by Lenovo Yoga Tab 3 Pro
 - bq27xxx: fix reporting critical level
 - syscon-reboot: add priority property support
 - Introduce new rt9467 charger driver
 - Introduce new rt9471 charger driver
 - Introduce new Odroid Go Ultra poweroff driver
 - misc. minor fixes and cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmP2liwACgkQ2O7X88g7
 +pqIYhAAn+V42O7Yf3w2FuUlxRvDUKfwi0V9LDWtHTxMQPR9WRZm2nHLJD2Ce4Ac
 9I8ZacQMfjLnKV+cP6QH7Q30QVNmqSlAdMa54sgACOY/M/eFjLVaNr56mhwFWPKN
 Z2Cel8ey87OpsxHHJ/9QrMuppVMGhv6VCtoLnvOWQ2PslTRIdoVlMxIhr5Ur047S
 hoHc3t9J5K1GX/j24g6ed7Iu9MZpag0lKfxDenVLqfFfoSdoDfWEdRQejtECfLjo
 Z7TtfvG3NBdJXlGOn/E475h/V5RDAckQw/se3m/GpCIk2YzY1Bg4V1PhR+wxPj07
 LT/DF5rfk8i9WflaUM9jQwhkZWzE8JqoH1Tsik5Yqbl15t389Vmef7Fh2pkMgxIF
 dyAzm2N3BhnA2ibUUW96n6swi/+VxbuFVde3xgc3nUx6Ug42FwN+PUnqTgwNp6GM
 h0oU/1xbflTlWD27Cu5ub77Y/CNg2TL06CKeLLi4CEs/Ldf2TDeviVZbNsAibasA
 bNBcDzHgs5jTH1FLAA79YKmbFbJWcM6rG0MA6KvzhNj/39eW/I99ehOScA3moIgN
 JdCZ3bCN0JQbTwt1QzUsT575F//QGLhYNeXrH9pG33EaXqpn4WRVN9ukxmLMSMBA
 UzmNGo7TUjofrDbh431XxDKcLmBaQZ9UFy0Z4lH6u+3B1nZ66RA=
 =bdNo
 -----END PGP SIGNATURE-----

Merge tag 'for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset updates from Sebastian Reichel:
 "Nothing special for the power-supply subystem this time.

   - power-supply core: remove faulty cooling logic

   - convert all sysfs show() handlers from *printf() use sysfs_emit()

   - bq25890: add dual-charger support required by Lenovo Yoga Tab 3 Pro

   - bq27xxx: fix reporting critical level

   - syscon-reboot: add priority property support

   - new rt9467 charger driver

   - new rt9471 charger driver

   - new Odroid Go Ultra poweroff driver

   - misc minor fixes and cleanups"

* tag 'for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (32 commits)
  power: reset: odroid-go-ultra: fix I2C dependency
  power: supply: leds: explicitly include linux/leds.h
  dt-bindings: power: supply: pm8941-coincell: Don't require charging properties
  dt-bindings: power: supply: pm8941-coincell: Add PM8998 compatible
  power: reset: add Odroid Go Ultra poweroff driver
  power: supply: rt9467: Fix spelling mistake "attache" -> "attach"
  power: supply: rt9471: fix using wrong ce_gpio in rt9471_probe()
  power: supply: max77650: Make max77650_charger_disable() return void
  Documentation: power: rt9467: Document exported sysfs entries
  power: supply: rt9467: Add Richtek RT9467 charger driver
  dt-bindings: power: supply: Add Richtek RT9467 battery charger
  Documentation: power: rt9471: Document exported sysfs entries
  power: supply: rt9471: Add Richtek RT9471 charger driver
  dt-bindings: power: supply: Add Richtek RT9471 battery charger
  power: supply: max1721x: Use strscpy() is more robust and safer
  power: supply: test-power: use strscpy() instead of strncpy()
  power: supply: bq27xxx: fix reporting critical level
  power: supply: bq256xx: Init ichg/vbat value with chip default value
  power: supply: collie_battery: Convert to GPIO descriptors (part 2)
  power: supply: remove faulty cooling logic
  ...
This commit is contained in:
Linus Torvalds 2023-02-24 17:04:23 -08:00
commit 39f013440d
42 changed files with 2909 additions and 233 deletions

View file

@ -0,0 +1,19 @@
What: /sys/class/power_supply/rt9467-*/sysoff_enable
Date: Feb 2023
KernelVersion: 6.3
Contact: ChiaEn Wu <chiaen_wu@richtek.com>
Description:
This entry allows enabling the sysoff mode of rt9467 charger
devices.
If enabled and the input is removed, the internal battery FET
is turned off to reduce the leakage from the BAT pin. See
device datasheet for details. It's commonly used when the
product enter shipping stage. After entering shipping mode,
only 'VBUS' or 'Power key" pressed can make it leave this mode.
'Disable' also can help to leave it, but it's more like to
abort the action before the device really enter shipping mode.
Access: Read, Write
Valid values:
- 1: enabled
- 0: disabled

View file

@ -0,0 +1,32 @@
What: /sys/class/power_supply/rt9471-*/sysoff_enable
Date: Feb 2023
KernelVersion: 6.3
Contact: ChiYuan Huang <cy_huang@richtek.com>
Description:
This entry allows enabling the sysoff mode of rt9471 charger devices.
If enabled and the input is removed, the internal battery FET is turned
off to reduce the leakage from the BAT pin. See device datasheet for details.
It's commonly used when the product enter shipping stage. After entering
shipping mode, only 'VBUS' or 'Power key" pressed can make it leave this
mode. 'Disable' also can help to leave it, but it's more like to abort
the action before the device really enter shipping mode.
Access: Read, Write
Valid values:
- 1: enabled
- 0: disabled
What: /sys/class/power_supply/rt9471-*/port_detect_enable
Date: Feb 2023
KernelVersion: 6.3
Contact: ChiYuan Huang <cy_huang@richtek.com>
Description:
This entry allows enabling the USB BC12 port detect function of rt9471 charger
devices. If enabled and VBUS is inserted, device will start to do the BC12
port detect and report the usb port type when port detect is done. See
datasheet for details. Normally controlled when TypeC/USBPD port integrated.
Access: Read, Write
Valid values:
- 1: enabled
- 0: disabled

View file

@ -42,6 +42,9 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: The reset value written to the reboot register (32 bit access).
priority:
default: 192
required:
- compatible
- offset
@ -49,6 +52,7 @@ required:
additionalProperties: false
allOf:
- $ref: restart-handler.yaml#
- if:
not:
required:

View file

@ -16,18 +16,30 @@ maintainers:
properties:
compatible:
const: qcom,pm8941-coincell
oneOf:
- items:
- enum:
- qcom,pm8998-coincell
- const: qcom,pm8941-coincell
- const: qcom,pm8941-coincell
reg:
maxItems: 1
qcom,rset-ohms:
description: resistance (in ohms) for current-limiting resistor
description: |
Resistance (in ohms) for current-limiting resistor. If unspecified,
inherit the previous configuration (e.g. from bootloader or hardware
default value).
enum: [ 800, 1200, 1700, 2100 ]
qcom,vset-millivolts:
$ref: /schemas/types.yaml#/definitions/uint32
description: voltage (in millivolts) to apply for charging
description: |
Voltage (in millivolts) to apply for charging. If unspecified, inherit
the previous configuration (e.g. from bootloader or hardware default
value).
enum: [ 2500, 3000, 3100, 3200 ]
qcom,charger-disable:
@ -37,8 +49,6 @@ properties:
required:
- compatible
- reg
- qcom,rset-ohms
- qcom,vset-millivolts
additionalProperties: false

View file

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/richtek,rt9467-charger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT9467 Switching Battery Charger with Power Path Management
maintainers:
- ChiYuan Huang <cy_huang@richtek.com>
- ChiaEn Wu <chiaen_wu@richtek.com>
description: |
RT9467 is a switch-mode single cell Li-Ion/Li-Polymer battery charger for
portable applications. It integrates a synchronous PWM controller, power
MOSFETs, input current sensing and regulation, high-accuracy voltage
regulation, and charge termination. The charge current is regulated through
integrated sensing resistors.
The RT9467 also features USB On-The-Go (OTG) support. It also integrates
D+/D- pin for USB host/charging port detection.
Datasheet is available at
https://www.richtek.com/assets/product_file/RT9467/DS9467-01.pdf
properties:
compatible:
const: richtek,rt9467-charger
reg:
maxItems: 1
wakeup-source: true
interrupts:
maxItems: 1
charge-enable-gpios:
description: GPIO is used to turn on and off charging.
maxItems: 1
usb-otg-vbus-regulator:
type: object
description: OTG boost regulator.
unevaluatedProperties: false
$ref: /schemas/regulator/regulator.yaml#
properties:
enable-gpios: true
required:
- compatible
- reg
- wakeup-source
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
charger@5b {
compatible = "richtek,rt9467-charger";
reg = <0x5b>;
wakeup-source;
interrupts-extended = <&gpio_intc 32 IRQ_TYPE_LEVEL_LOW>;
charge-enable-gpios = <&gpio26 1 GPIO_ACTIVE_LOW>;
rt9467_otg_vbus: usb-otg-vbus-regulator {
regulator-name = "rt9467-usb-otg-vbus";
regulator-min-microvolt = <4425000>;
regulator-max-microvolt = <5825000>;
regulator-min-microamp = <500000>;
regulator-max-microamp = <3000000>;
};
};
};

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/richtek,rt9471.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT9471 3A Single Cell Switching Battery charger
maintainers:
- Alina Yu <alina_yu@richtek.com>
- ChiYuan Huang <cy_huang@richtek.com>
description: |
RT9471 is a switch-mode single cell Li-Ion/Li-Polymer battery charger for
portable applications. It supports USB BC1.2 port detection, current and
voltage regulations in both charging and boost mode.
Datasheet is available at
https://www.richtek.com/assets/product_file/RT9471=RT9471D/DS9471D-02.pdf
properties:
compatible:
const: richtek,rt9471
reg:
maxItems: 1
charge-enable-gpios:
description: GPIO used to turn on and off charging.
maxItems: 1
wakeup-source: true
interrupts:
maxItems: 1
usb-otg-vbus-regulator:
type: object
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
- wakeup-source
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
charger@53 {
compatible = "richtek,rt9471";
reg = <0x53>;
charge-enable-gpios = <&gpio26 1 GPIO_ACTIVE_LOW>;
wakeup-source;
interrupts-extended = <&gpio_intc 32 IRQ_TYPE_EDGE_FALLING>;
usb-otg-vbus-regulator {
regulator-name = "usb-otg-vbus";
regulator-min-microvolt = <4850000>;
regulator-max-microvolt = <5300000>;
regulator-min-microamp = <500000>;
regulator-max-microamp = <1200000>;
};
};
};

View file

@ -187,7 +187,7 @@ struct x86_dev_info {
/* Generic / shared charger / battery settings */
static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" };
static const char * const bq24190_psy[] = { "bq24190-charger" };
static const char * const bq25890_psy[] = { "bq25890-charger" };
static const char * const bq25890_psy[] = { "bq25890-charger-0" };
static const struct property_entry fg_bq24190_supply_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy),

View file

@ -141,6 +141,13 @@ config POWER_RESET_OCELOT_RESET
help
This driver supports restart for Microsemi Ocelot SoC and similar.
config POWER_RESET_ODROID_GO_ULTRA_POWEROFF
bool "Odroid Go Ultra power-off driver"
depends on ARCH_MESON || COMPILE_TEST
depends on I2C=y && OF
help
This driver supports Power off for Odroid Go Ultra device.
config POWER_RESET_OXNAS
bool "OXNAS SoC restart driver"
depends on ARCH_OXNAS

View file

@ -17,6 +17,7 @@ obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o
obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o

View file

@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_platform.h>
#include <linux/mfd/rk808.h>
#include <linux/regmap.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/i2c.h>
/*
* The Odroid Go Ultra has 2 PMICs:
* - RK818 (manages the battery and USB-C power supply)
* - RK817
* Both PMICs feeds power to the S922X SoC, so they must be powered-off in sequence.
* Vendor does power-off the RK817 first, then the RK818 so here we follow this sequence.
*/
struct odroid_go_ultra_poweroff_data {
struct device *dev;
struct device *rk817;
struct device *rk818;
};
static int odroid_go_ultra_poweroff_prepare(struct sys_off_data *data)
{
struct odroid_go_ultra_poweroff_data *poweroff_data = data->cb_data;
struct regmap *rk817, *rk818;
int ret;
/* RK817 Regmap */
rk817 = dev_get_regmap(poweroff_data->rk817, NULL);
if (!rk817) {
dev_err(poweroff_data->dev, "failed to get rk817 regmap\n");
return notifier_from_errno(-EINVAL);
}
/* RK818 Regmap */
rk818 = dev_get_regmap(poweroff_data->rk818, NULL);
if (!rk818) {
dev_err(poweroff_data->dev, "failed to get rk818 regmap\n");
return notifier_from_errno(-EINVAL);
}
dev_info(poweroff_data->dev, "Setting PMICs for power off");
/* RK817 */
ret = regmap_update_bits(rk817, RK817_SYS_CFG(3), DEV_OFF, DEV_OFF);
if (ret) {
dev_err(poweroff_data->dev, "failed to poweroff rk817\n");
return notifier_from_errno(ret);
}
/* RK818 */
ret = regmap_update_bits(rk818, RK818_DEVCTRL_REG, DEV_OFF, DEV_OFF);
if (ret) {
dev_err(poweroff_data->dev, "failed to poweroff rk818\n");
return notifier_from_errno(ret);
}
return NOTIFY_OK;
}
static void odroid_go_ultra_poweroff_put_pmic_device(void *data)
{
struct device *dev = data;
put_device(dev);
}
static int odroid_go_ultra_poweroff_get_pmic_device(struct device *dev, const char *compatible,
struct device **pmic)
{
struct device_node *pmic_node;
struct i2c_client *pmic_client;
pmic_node = of_find_compatible_node(NULL, NULL, compatible);
if (!pmic_node)
return -ENODEV;
pmic_client = of_find_i2c_device_by_node(pmic_node);
of_node_put(pmic_node);
if (!pmic_client)
return -EPROBE_DEFER;
*pmic = &pmic_client->dev;
return devm_add_action_or_reset(dev, odroid_go_ultra_poweroff_put_pmic_device, *pmic);
}
static int odroid_go_ultra_poweroff_probe(struct platform_device *pdev)
{
struct odroid_go_ultra_poweroff_data *poweroff_data;
int ret;
poweroff_data = devm_kzalloc(&pdev->dev, sizeof(*poweroff_data), GFP_KERNEL);
if (!poweroff_data)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, poweroff_data);
/* RK818 PMIC Device */
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk818",
&poweroff_data->rk818);
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to get rk818 mfd data\n");
/* RK817 PMIC Device */
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk817",
&poweroff_data->rk817);
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to get rk817 mfd data\n");
/* Register as SYS_OFF_MODE_POWER_OFF_PREPARE because regmap_update_bits may sleep */
ret = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_POWER_OFF_PREPARE,
SYS_OFF_PRIO_DEFAULT,
odroid_go_ultra_poweroff_prepare,
poweroff_data);
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to register sys-off handler\n");
dev_info(&pdev->dev, "Registered Power-Off handler\n");
return 0;
}
static struct platform_device *pdev;
static struct platform_driver odroid_go_ultra_poweroff_driver = {
.driver = {
.name = "odroid-go-ultra-poweroff",
},
.probe = odroid_go_ultra_poweroff_probe,
};
static int __init odroid_go_ultra_poweroff_init(void)
{
int ret;
/* Only create when running on the Odroid Go Ultra device */
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra"))
return -ENODEV;
ret = platform_driver_register(&odroid_go_ultra_poweroff_driver);
if (ret)
return ret;
pdev = platform_device_register_resndata(NULL, "odroid-go-ultra-poweroff", -1,
NULL, 0, NULL, 0);
if (IS_ERR(pdev)) {
platform_driver_unregister(&odroid_go_ultra_poweroff_driver);
return PTR_ERR(pdev);
}
return 0;
}
static void __exit odroid_go_ultra_poweroff_exit(void)
{
/* Only delete when running on the Odroid Go Ultra device */
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra"))
return;
platform_device_unregister(pdev);
platform_driver_unregister(&odroid_go_ultra_poweroff_driver);
}
module_init(odroid_go_ultra_poweroff_init);
module_exit(odroid_go_ultra_poweroff_exit);
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
MODULE_DESCRIPTION("Odroid Go Ultra poweroff driver");
MODULE_LICENSE("GPL");

View file

@ -44,6 +44,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
struct syscon_reboot_context *ctx;
struct device *dev = &pdev->dev;
int mask_err, value_err;
int priority;
int err;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@ -57,6 +58,9 @@ static int syscon_reboot_probe(struct platform_device *pdev)
return PTR_ERR(ctx->map);
}
if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
priority = 192;
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
return -EINVAL;
@ -77,7 +81,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
}
ctx->restart_handler.notifier_call = syscon_restart_handle;
ctx->restart_handler.priority = 192;
ctx->restart_handler.priority = priority;
err = register_restart_handler(&ctx->restart_handler);
if (err)
dev_err(dev, "can't register restart notifier (err=%d)\n", err);

View file

@ -765,6 +765,41 @@ config CHARGER_RT9455
help
Say Y to enable support for Richtek RT9455 battery charger.
config CHARGER_RT9467
tristate "Richtek RT9467 Battery Charger Driver"
depends on I2C && GPIOLIB && REGULATOR
select REGMAP_I2C
select REGMAP_IRQ
select LINEAR_RANGES
help
Say Y here to enable RT9467 Battery Charger.
RT9467 is a switch-mode single cell Li-Ion/Li-Polymer battery charger
for portable applications. It integrates a synchronous PWM controller,
power MOSFETs, input current sensing and regulation, high-accuracy
voltage regulation, and charge termination. The charge current is
regulated through integrated sensing resistors. It also features
USB On-The-Go (OTG) support and integrates D+/D- pin for USB
host/charging port detection.
This driver can also be built as a module. If so, the module
will be called "rt9467-charger".
config CHARGER_RT9471
tristate "Richtek RT9471 battery charger driver"
depends on I2C && GPIOLIB && REGULATOR
select REGMAP_I2C
select REGMAP_IRQ
select LINEAR_RANGES
help
This adds support for Richtek RT9471 battery charger. RT9471 is
highly-integrated switch mode battery charger which is system power
patch manageable device for single cell Li-Ion and Li-polymer battery.
It can support BC12 detection on DPDM, and current and voltage
regulation on both charging and boost mode.
This driver can also be built as a module. If so, the module will be
called rt9471.
config CHARGER_CROS_USBPD
tristate "ChromeOS EC based USBPD charger"
depends on CROS_USBPD_NOTIFY

View file

@ -54,6 +54,8 @@ obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o
obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o
obj-$(CONFIG_CHARGER_RT9471) += rt9471.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o

View file

@ -2453,7 +2453,7 @@ struct ab8500_fg_sysfs_entry {
static ssize_t charge_full_show(struct ab8500_fg *di, char *buf)
{
return sprintf(buf, "%d\n", di->bat_cap.max_mah);
return sysfs_emit(buf, "%d\n", di->bat_cap.max_mah);
}
static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
@ -2472,7 +2472,7 @@ static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
{
return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
return sysfs_emit(buf, "%d\n", di->bat_cap.prev_mah);
}
static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
@ -2594,7 +2594,7 @@ static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
return sysfs_emit(buf, "%d\n", (reg_value & 0x7F));
fail:
return ret;
@ -2644,7 +2644,7 @@ static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
return sysfs_emit(buf, "%d\n", (reg_value & 0x7F));
fail:
return ret;
@ -2695,7 +2695,7 @@ static ssize_t ab8505_powercut_restart_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
return sysfs_emit(buf, "%d\n", (reg_value & 0xF));
fail:
return ret;
@ -2746,7 +2746,7 @@ static ssize_t ab8505_powercut_timer_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
return sysfs_emit(buf, "%d\n", (reg_value & 0x7F));
fail:
return ret;
@ -2769,7 +2769,7 @@ static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
return sysfs_emit(buf, "%d\n", (reg_value & 0xF0) >> 4);
fail:
return ret;
@ -2790,7 +2790,7 @@ static ssize_t ab8505_powercut_read(struct device *dev,
if (ret < 0)
goto fail;
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
return sysfs_emit(buf, "%d\n", (reg_value & 0x1));
fail:
return ret;
@ -2841,7 +2841,7 @@ static ssize_t ab8505_powercut_flag_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
return sysfs_emit(buf, "%d\n", ((reg_value & 0x10) >> 4));
fail:
return ret;
@ -2864,7 +2864,7 @@ static ssize_t ab8505_powercut_debounce_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
return sysfs_emit(buf, "%d\n", (reg_value & 0x7));
fail:
return ret;
@ -2914,7 +2914,7 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
goto fail;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
return sysfs_emit(buf, "%d\n", ((reg_value & 0x20) >> 5));
fail:
return ret;

View file

@ -1059,7 +1059,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev,
ret = bq2415x_exec_command(bq, command);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
/*
@ -1098,11 +1098,11 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
if (bq->timer_error)
return sprintf(buf, "%s\n", bq->timer_error);
return sysfs_emit(buf, "%s\n", bq->timer_error);
if (bq->autotimer)
return sprintf(buf, "auto\n");
return sprintf(buf, "off\n");
return sysfs_emit(buf, "auto\n");
return sysfs_emit(buf, "off\n");
}
/*
@ -1175,30 +1175,30 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
ssize_t ret = 0;
if (bq->automode > 0)
ret += sprintf(buf+ret, "auto (");
ret += sysfs_emit_at(buf, ret, "auto (");
switch (bq->mode) {
case BQ2415X_MODE_OFF:
ret += sprintf(buf+ret, "off");
ret += sysfs_emit_at(buf, ret, "off");
break;
case BQ2415X_MODE_NONE:
ret += sprintf(buf+ret, "none");
ret += sysfs_emit_at(buf, ret, "none");
break;
case BQ2415X_MODE_HOST_CHARGER:
ret += sprintf(buf+ret, "host");
ret += sysfs_emit_at(buf, ret, "host");
break;
case BQ2415X_MODE_DEDICATED_CHARGER:
ret += sprintf(buf+ret, "dedicated");
ret += sysfs_emit_at(buf, ret, "dedicated");
break;
case BQ2415X_MODE_BOOST:
ret += sprintf(buf+ret, "boost");
ret += sysfs_emit_at(buf, ret, "boost");
break;
}
if (bq->automode > 0)
ret += sprintf(buf+ret, ")");
ret += sysfs_emit_at(buf, ret, ")");
ret += sprintf(buf+ret, "\n");
ret += sysfs_emit_at(buf, ret, "\n");
return ret;
}
@ -1215,15 +1215,15 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
switch (bq->reported_mode) {
case BQ2415X_MODE_OFF:
return sprintf(buf, "off\n");
return sysfs_emit(buf, "off\n");
case BQ2415X_MODE_NONE:
return sprintf(buf, "none\n");
return sysfs_emit(buf, "none\n");
case BQ2415X_MODE_HOST_CHARGER:
return sprintf(buf, "host\n");
return sysfs_emit(buf, "host\n");
case BQ2415X_MODE_DEDICATED_CHARGER:
return sprintf(buf, "dedicated\n");
return sysfs_emit(buf, "dedicated\n");
case BQ2415X_MODE_BOOST:
return sprintf(buf, "boost\n");
return sysfs_emit(buf, "boost\n");
}
return -EINVAL;
@ -1261,8 +1261,8 @@ static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
int ret = bq2415x_i2c_read(bq, reg);
if (ret < 0)
return sprintf(buf, "%#.2x=error %d\n", reg, ret);
return sprintf(buf, "%#.2x=%#.2x\n", reg, ret);
return sysfs_emit(buf, "%#.2x=error %d\n", reg, ret);
return sysfs_emit(buf, "%#.2x=%#.2x\n", reg, ret);
}
/* show all raw values of chip register, format per line: 'register=value' */
@ -1338,7 +1338,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
/* set *_enable entries */
@ -1401,7 +1401,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
ret = bq2415x_exec_command(bq, command);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,

View file

@ -463,7 +463,7 @@ static ssize_t bq24190_sysfs_show(struct device *dev,
if (ret)
count = ret;
else
count = scnprintf(buf, PAGE_SIZE, "%hhx\n", v);
count = sysfs_emit(buf, "%hhx\n", v);
pm_runtime_mark_last_busy(bdi->dev);
pm_runtime_put_autosuspend(bdi->dev);

View file

@ -767,8 +767,7 @@ static ssize_t bq24257_show_ovp_voltage(struct device *dev,
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return scnprintf(buf, PAGE_SIZE, "%u\n",
bq24257_vovp_map[bq->init_data.vovp]);
return sysfs_emit(buf, "%u\n", bq24257_vovp_map[bq->init_data.vovp]);
}
static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
@ -778,8 +777,7 @@ static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return scnprintf(buf, PAGE_SIZE, "%u\n",
bq24257_vindpm_map[bq->init_data.vindpm]);
return sysfs_emit(buf, "%u\n", bq24257_vindpm_map[bq->init_data.vindpm]);
}
static ssize_t bq24257_sysfs_show_enable(struct device *dev,
@ -800,7 +798,7 @@ static ssize_t bq24257_sysfs_show_enable(struct device *dev,
if (ret < 0)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t bq24257_sysfs_set_enable(struct device *dev,

View file

@ -1563,7 +1563,7 @@ static int bq256xx_hw_init(struct bq256xx_device *bq)
return ret;
ret = bq->chip_info->bq256xx_set_ichg(bq,
bat_info->constant_charge_current_max_ua);
bq->chip_info->bq256xx_def_ichg);
if (ret)
return ret;
@ -1573,7 +1573,7 @@ static int bq256xx_hw_init(struct bq256xx_device *bq)
return ret;
ret = bq->chip_info->bq256xx_set_vbatreg(bq,
bat_info->constant_charge_voltage_max_uv);
bq->chip_info->bq256xx_def_vbatreg);
if (ret)
return ret;

View file

@ -95,6 +95,7 @@ struct bq25890_init_data {
struct bq25890_state {
u8 online;
u8 hiz;
u8 chrg_status;
u8 chrg_fault;
u8 vsys_status;
@ -107,6 +108,10 @@ struct bq25890_device {
struct i2c_client *client;
struct device *dev;
struct power_supply *charger;
struct power_supply *secondary_chrg;
struct power_supply_desc desc;
char name[28]; /* "bq25890-charger-%d" */
int id;
struct usb_phy *usb_phy;
struct notifier_block usb_nb;
@ -119,7 +124,9 @@ struct bq25890_device {
bool skip_reset;
bool read_back_init_data;
bool force_hiz;
u32 pump_express_vbus_max;
u32 iinlim_percentage;
enum bq25890_chip_version chip_version;
struct bq25890_init_data init_data;
struct bq25890_state state;
@ -127,6 +134,9 @@ struct bq25890_device {
struct mutex lock; /* protect state data */
};
static DEFINE_IDR(bq25890_id);
static DEFINE_MUTEX(bq25890_id_mutex);
static const struct regmap_range bq25890_readonly_reg_ranges[] = {
regmap_reg_range(0x0b, 0x0c),
regmap_reg_range(0x0e, 0x13),
@ -454,20 +464,18 @@ static int bq25890_get_vbus_voltage(struct bq25890_device *bq)
return bq25890_find_val(ret, TBL_VBUSV);
}
static int bq25890_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
static void bq25890_update_state(struct bq25890_device *bq,
enum power_supply_property psp,
struct bq25890_state *state)
{
struct bq25890_device *bq = power_supply_get_drvdata(psy);
struct bq25890_state state;
bool do_adc_conv;
int ret;
mutex_lock(&bq->lock);
/* update state in case we lost an interrupt */
__bq25890_handle_irq(bq);
state = bq->state;
do_adc_conv = !state.online && bq25890_is_adc_property(psp);
*state = bq->state;
do_adc_conv = (!state->online || state->hiz) && bq25890_is_adc_property(psp);
if (do_adc_conv)
bq25890_field_write(bq, F_CONV_START, 1);
mutex_unlock(&bq->lock);
@ -475,10 +483,21 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
if (do_adc_conv)
regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START],
ret, !ret, 25000, 1000000);
}
static int bq25890_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct bq25890_device *bq = power_supply_get_drvdata(psy);
struct bq25890_state state;
int ret;
bq25890_update_state(bq, psp, &state);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (!state.online)
if (!state.online || state.hiz)
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (state.chrg_status == STATUS_NOT_CHARGING)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
@ -493,7 +512,8 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
if (!state.online || state.chrg_status == STATUS_NOT_CHARGING ||
if (!state.online || state.hiz ||
state.chrg_status == STATUS_NOT_CHARGING ||
state.chrg_status == STATUS_TERMINATION_DONE)
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
else if (state.chrg_status == STATUS_PRE_CHARGING)
@ -513,7 +533,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = state.online;
val->intval = state.online && !state.hiz;
break;
case POWER_SUPPLY_PROP_HEALTH:
@ -667,7 +687,8 @@ static int bq25890_power_supply_set_property(struct power_supply *psy,
const union power_supply_propval *val)
{
struct bq25890_device *bq = power_supply_get_drvdata(psy);
int maxval;
struct bq25890_state state;
int maxval, ret;
u8 lval;
switch (psp) {
@ -682,6 +703,12 @@ static int bq25890_power_supply_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
lval = bq25890_find_idx(val->intval, TBL_IINLIM);
return bq25890_field_write(bq, F_IINLIM, lval);
case POWER_SUPPLY_PROP_ONLINE:
ret = bq25890_field_write(bq, F_EN_HIZ, !val->intval);
if (!ret)
bq->force_hiz = !val->intval;
bq25890_update_state(bq, psp, &state);
return ret;
default:
return -EINVAL;
}
@ -694,12 +721,25 @@ static int bq25890_power_supply_property_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_ONLINE:
return true;
default:
return false;
}
}
/*
* If there are multiple chargers the maximum current the external power-supply
* can deliver needs to be divided over the chargers. This is done according
* to the bq->iinlim_percentage setting.
*/
static int bq25890_charger_get_scaled_iinlim_regval(struct bq25890_device *bq,
int iinlim_ua)
{
iinlim_ua = iinlim_ua * bq->iinlim_percentage / 100;
return bq25890_find_idx(iinlim_ua, TBL_IINLIM);
}
/* On the BQ25892 try to get charger-type info from our supplier */
static void bq25890_charger_external_power_changed(struct power_supply *psy)
{
@ -718,7 +758,7 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy)
switch (val.intval) {
case POWER_SUPPLY_USB_TYPE_DCP:
input_current_limit = bq25890_find_idx(2000000, TBL_IINLIM);
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 2000000);
if (bq->pump_express_vbus_max) {
queue_delayed_work(system_power_efficient_wq,
&bq->pump_express_work,
@ -727,11 +767,11 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy)
break;
case POWER_SUPPLY_USB_TYPE_CDP:
case POWER_SUPPLY_USB_TYPE_ACA:
input_current_limit = bq25890_find_idx(1500000, TBL_IINLIM);
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 1500000);
break;
case POWER_SUPPLY_USB_TYPE_SDP:
default:
input_current_limit = bq25890_find_idx(500000, TBL_IINLIM);
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 500000);
}
bq25890_field_write(bq, F_IINLIM, input_current_limit);
@ -748,6 +788,7 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
} state_fields[] = {
{F_CHG_STAT, &state->chrg_status},
{F_PG_STAT, &state->online},
{F_EN_HIZ, &state->hiz},
{F_VSYS_STAT, &state->vsys_status},
{F_BOOST_FAULT, &state->boost_fault},
{F_BAT_FAULT, &state->bat_fault},
@ -763,16 +804,18 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
*state_fields[i].data = ret;
}
dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n",
state->chrg_status, state->online, state->vsys_status,
state->chrg_fault, state->boost_fault, state->bat_fault,
state->ntc_fault);
dev_dbg(bq->dev, "S:CHG/PG/HIZ/VSYS=%d/%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n",
state->chrg_status, state->online,
state->hiz, state->vsys_status,
state->chrg_fault, state->boost_fault,
state->bat_fault, state->ntc_fault);
return 0;
}
static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq)
{
bool adc_conv_rate, new_adc_conv_rate;
struct bq25890_state new_state;
int ret;
@ -783,14 +826,23 @@ static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq)
if (!memcmp(&bq->state, &new_state, sizeof(new_state)))
return IRQ_NONE;
if (!new_state.online && bq->state.online) { /* power removed */
/* disable ADC */
ret = bq25890_field_write(bq, F_CONV_RATE, 0);
/*
* Restore HiZ bit in case it was set by user. The chip does not retain
* this bit on cable replug, hence the bit must be reset manually here.
*/
if (new_state.online && !bq->state.online && bq->force_hiz) {
ret = bq25890_field_write(bq, F_EN_HIZ, bq->force_hiz);
if (ret < 0)
goto error;
} else if (new_state.online && !bq->state.online) { /* power inserted */
/* enable ADC, to have control of charge current/voltage */
ret = bq25890_field_write(bq, F_CONV_RATE, 1);
new_state.hiz = 1;
}
/* Should period ADC sampling be enabled? */
adc_conv_rate = bq->state.online && !bq->state.hiz;
new_adc_conv_rate = new_state.online && !new_state.hiz;
if (new_adc_conv_rate != adc_conv_rate) {
ret = bq25890_field_write(bq, F_CONV_RATE, new_adc_conv_rate);
if (ret < 0)
goto error;
}
@ -924,7 +976,7 @@ static int bq25890_hw_init(struct bq25890_device *bq)
}
/* Configure ADC for continuous conversions when charging */
ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online);
ret = bq25890_field_write(bq, F_CONV_RATE, bq->state.online && !bq->state.hiz);
if (ret < 0) {
dev_dbg(bq->dev, "Config ADC failed %d\n", ret);
return ret;
@ -957,7 +1009,6 @@ static char *bq25890_charger_supplied_to[] = {
};
static const struct power_supply_desc bq25890_power_supply_desc = {
.name = "bq25890-charger",
.type = POWER_SUPPLY_TYPE_USB,
.properties = bq25890_power_supply_props,
.num_properties = ARRAY_SIZE(bq25890_power_supply_props),
@ -971,12 +1022,21 @@ static int bq25890_power_supply_init(struct bq25890_device *bq)
{
struct power_supply_config psy_cfg = { .drv_data = bq, };
/* Get ID for the device */
mutex_lock(&bq25890_id_mutex);
bq->id = idr_alloc(&bq25890_id, bq, 0, 0, GFP_KERNEL);
mutex_unlock(&bq25890_id_mutex);
if (bq->id < 0)
return bq->id;
snprintf(bq->name, sizeof(bq->name), "bq25890-charger-%d", bq->id);
bq->desc = bq25890_power_supply_desc;
bq->desc.name = bq->name;
psy_cfg.supplied_to = bq25890_charger_supplied_to;
psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
bq->charger = devm_power_supply_register(bq->dev,
&bq25890_power_supply_desc,
&psy_cfg);
bq->charger = devm_power_supply_register(bq->dev, &bq->desc, &psy_cfg);
return PTR_ERR_OR_ZERO(bq->charger);
}
@ -996,10 +1056,17 @@ static void bq25890_pump_express_work(struct work_struct *data)
{
struct bq25890_device *bq =
container_of(data, struct bq25890_device, pump_express_work.work);
union power_supply_propval value;
int voltage, i, ret;
dev_dbg(bq->dev, "Start to request input voltage increasing\n");
/* If there is a second charger put in Hi-Z mode */
if (bq->secondary_chrg) {
value.intval = 0;
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value);
}
/* Enable current pulse voltage control protocol */
ret = bq25890_field_write(bq, F_PUMPX_EN, 1);
if (ret < 0)
@ -1031,6 +1098,11 @@ static void bq25890_pump_express_work(struct work_struct *data)
bq25890_field_write(bq, F_PUMPX_EN, 0);
if (bq->secondary_chrg) {
value.intval = 1;
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value);
}
dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
voltage);
@ -1077,6 +1149,17 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
static int bq25890_vbus_enable(struct regulator_dev *rdev)
{
struct bq25890_device *bq = rdev_get_drvdata(rdev);
union power_supply_propval val = {
.intval = 0,
};
/*
* When enabling 5V boost / Vbus output, we need to put the secondary
* charger in Hi-Z mode to avoid it trying to charge the secondary
* battery from the 5V boost output.
*/
if (bq->secondary_chrg)
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val);
return bq25890_set_otg_cfg(bq, 1);
}
@ -1084,8 +1167,19 @@ static int bq25890_vbus_enable(struct regulator_dev *rdev)
static int bq25890_vbus_disable(struct regulator_dev *rdev)
{
struct bq25890_device *bq = rdev_get_drvdata(rdev);
union power_supply_propval val = {
.intval = 1,
};
int ret;
return bq25890_set_otg_cfg(bq, 0);
ret = bq25890_set_otg_cfg(bq, 0);
if (ret)
return ret;
if (bq->secondary_chrg)
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val);
return 0;
}
static int bq25890_vbus_is_enabled(struct regulator_dev *rdev)
@ -1296,11 +1390,31 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
{
int ret;
struct bq25890_init_data *init = &bq->init_data;
const char *str;
u32 val;
ret = device_property_read_string(bq->dev, "linux,secondary-charger-name", &str);
if (ret == 0) {
bq->secondary_chrg = power_supply_get_by_name(str);
if (!bq->secondary_chrg)
return -EPROBE_DEFER;
}
/* Optional, left at 0 if property is not present */
device_property_read_u32(bq->dev, "linux,pump-express-vbus-max",
&bq->pump_express_vbus_max);
ret = device_property_read_u32(bq->dev, "linux,iinlim-percentage", &val);
if (ret == 0) {
if (val > 100) {
dev_err(bq->dev, "Error linux,iinlim-percentage %u > 100\n", val);
return -EINVAL;
}
bq->iinlim_percentage = val;
} else {
bq->iinlim_percentage = 100;
}
bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset");
bq->read_back_init_data = device_property_read_bool(bq->dev,
"linux,read-back-settings");
@ -1322,6 +1436,12 @@ static void bq25890_non_devm_cleanup(void *data)
struct bq25890_device *bq = data;
cancel_delayed_work_sync(&bq->pump_express_work);
if (bq->id >= 0) {
mutex_lock(&bq25890_id_mutex);
idr_remove(&bq25890_id, bq->id);
mutex_unlock(&bq25890_id_mutex);
}
}
static int bq25890_probe(struct i2c_client *client)
@ -1336,6 +1456,7 @@ static int bq25890_probe(struct i2c_client *client)
bq->client = client;
bq->dev = dev;
bq->id = -1;
mutex_init(&bq->lock);
INIT_DELAYED_WORK(&bq->pump_express_work, bq25890_pump_express_work);

View file

@ -1917,10 +1917,10 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
if (di->opts & BQ27XXX_O_ZERO) {
if (di->cache.flags & BQ27000_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27000_FLAG_EDV1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (di->cache.flags & BQ27000_FLAG_EDVF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else if (di->cache.flags & BQ27000_FLAG_EDV1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
} else if (di->opts & BQ27Z561_O_BITS) {
@ -1933,10 +1933,10 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
} else {
if (di->cache.flags & BQ27XXX_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
}

View file

@ -1075,7 +1075,7 @@ static ssize_t charger_name_show(struct device *dev,
struct charger_regulator *charger
= container_of(attr, struct charger_regulator, attr_name);
return sprintf(buf, "%s\n", charger->regulator_name);
return sysfs_emit(buf, "%s\n", charger->regulator_name);
}
static ssize_t charger_state_show(struct device *dev,
@ -1088,7 +1088,7 @@ static ssize_t charger_state_show(struct device *dev,
if (!charger->externally_control)
state = regulator_is_enabled(charger->consumer);
return sprintf(buf, "%s\n", state ? "enabled" : "disabled");
return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled");
}
static ssize_t charger_externally_control_show(struct device *dev,
@ -1097,7 +1097,7 @@ static ssize_t charger_externally_control_show(struct device *dev,
struct charger_regulator *charger = container_of(attr,
struct charger_regulator, attr_externally_control);
return sprintf(buf, "%d\n", charger->externally_control);
return sysfs_emit(buf, "%d\n", charger->externally_control);
}
static ssize_t charger_externally_control_store(struct device *dev,

View file

@ -404,7 +404,7 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
goto err_psy_reg_bu;
}
ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
ret = request_irq(gpiod_to_irq(collie_bat_main.gpio_full),
collie_bat_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"main full", &collie_bat_main);
@ -440,7 +440,7 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
static void collie_bat_remove(struct ucb1x00_dev *dev)
{
free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main);
power_supply_unregister(collie_bat_bu.psy);
power_supply_unregister(collie_bat_main.psy);

View file

@ -466,10 +466,8 @@ static int da9150_charger_register_irq(struct platform_device *pdev,
int irq, ret;
irq = platform_get_irq_byname(pdev, irq_name);
if (irq < 0) {
dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
if (irq < 0)
return irq;
}
ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name,
charger);
@ -482,15 +480,12 @@ static int da9150_charger_register_irq(struct platform_device *pdev,
static void da9150_charger_unregister_irq(struct platform_device *pdev,
const char *irq_name)
{
struct device *dev = &pdev->dev;
struct da9150_charger *charger = platform_get_drvdata(pdev);
int irq;
irq = platform_get_irq_byname(pdev, irq_name);
if (irq < 0) {
dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
if (irq < 0)
return;
}
free_irq(irq, charger);
}

View file

@ -454,7 +454,7 @@ static ssize_t ds2780_get_pmod_enabled(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n",
return sysfs_emit(buf, "%d\n",
!!(control_reg & DS2780_CONTROL_REG_PMOD));
}
@ -507,7 +507,7 @@ static ssize_t ds2780_get_sense_resistor_value(struct device *dev,
if (ret < 0)
return ret;
ret = sprintf(buf, "%d\n", sense_resistor);
ret = sysfs_emit(buf, "%d\n", sense_resistor);
return ret;
}
@ -545,7 +545,7 @@ static ssize_t ds2780_get_rsgain_setting(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", rsgain);
return sysfs_emit(buf, "%d\n", rsgain);
}
static ssize_t ds2780_set_rsgain_setting(struct device *dev,
@ -588,7 +588,7 @@ static ssize_t ds2780_get_pio_pin(struct device *dev,
if (ret < 0)
return ret;
ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC);
ret = sysfs_emit(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC);
return ret;
}

View file

@ -456,7 +456,7 @@ static ssize_t ds2781_get_pmod_enabled(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n",
return sysfs_emit(buf, "%d\n",
!!(control_reg & DS2781_CONTROL_PMOD));
}
@ -509,7 +509,7 @@ static ssize_t ds2781_get_sense_resistor_value(struct device *dev,
if (ret < 0)
return ret;
ret = sprintf(buf, "%d\n", sense_resistor);
ret = sysfs_emit(buf, "%d\n", sense_resistor);
return ret;
}
@ -547,7 +547,7 @@ static ssize_t ds2781_get_rsgain_setting(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", rsgain);
return sysfs_emit(buf, "%d\n", rsgain);
}
static ssize_t ds2781_set_rsgain_setting(struct device *dev,
@ -590,7 +590,7 @@ static ssize_t ds2781_get_pio_pin(struct device *dev,
if (ret < 0)
return ret;
ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
ret = sysfs_emit(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
return ret;
}

View file

@ -602,7 +602,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev,
lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]);
return sysfs_emit(buf, "%s\n", desc[state]);
}
static ssize_t lp8788_show_eoc_time(struct device *dev,
@ -618,8 +618,7 @@ static ssize_t lp8788_show_eoc_time(struct device *dev,
lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n",
stime[val]);
return sysfs_emit(buf, "End Of Charge Time: %s\n", stime[val]);
}
static ssize_t lp8788_show_eoc_level(struct device *dev,
@ -642,7 +641,7 @@ static ssize_t lp8788_show_eoc_level(struct device *dev,
val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
level = mode ? abs_level[val] : relative_level[val];
return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level);
return sysfs_emit(buf, "End Of Charge Level: %s\n", level);
}
static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);

View file

@ -525,7 +525,7 @@ static ssize_t charge_status_show(struct device *dev,
}
}
return sprintf(buf, "%s\n", result);
return sysfs_emit(buf, "%s\n", result);
}
static DEVICE_ATTR_RO(charge_status);
@ -541,7 +541,7 @@ static ssize_t vbat_show(struct device *dev,
if (ret)
return ret;
return sprintf(buf, "%d\n", val.intval);
return sysfs_emit(buf, "%d\n", val.intval);
}
static DEVICE_ATTR_RO(vbat);
@ -557,7 +557,7 @@ static ssize_t vbat_avg_show(struct device *dev,
if (ret)
return ret;
return sprintf(buf, "%d\n", val.intval);
return sysfs_emit(buf, "%d\n", val.intval);
}
static DEVICE_ATTR_RO(vbat_avg);
@ -573,7 +573,7 @@ static ssize_t ibat_show(struct device *dev,
if (ret)
return ret;
return sprintf(buf, "%d\n", val.intval);
return sysfs_emit(buf, "%d\n", val.intval);
}
static DEVICE_ATTR_RO(ibat);
@ -589,7 +589,7 @@ static ssize_t force_telemetry_show(struct device *dev,
if (ret)
return ret;
return sprintf(buf, "%u\n", regval & BIT(2) ? 1 : 0);
return sysfs_emit(buf, "%u\n", regval & BIT(2) ? 1 : 0);
}
static ssize_t force_telemetry_store(struct device *dev,
@ -628,7 +628,7 @@ static ssize_t arm_ship_mode_show(struct device *dev,
if (ret)
return ret;
return sprintf(buf, "%u\n",
return sysfs_emit(buf, "%u\n",
regval == LTC4162L_ARM_SHIP_MODE_MAGIC ? 1 : 0);
}

View file

@ -532,7 +532,7 @@ static ssize_t show_fast_charge_timer(struct device *dev,
break;
}
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
return sysfs_emit(buf, "%u\n", val);
}
static ssize_t store_fast_charge_timer(struct device *dev,

View file

@ -384,7 +384,7 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl)
}
if (!info->ManufacturerName[0])
strncpy(info->ManufacturerName, DEF_MFG_NAME,
strscpy(info->ManufacturerName, DEF_MFG_NAME,
2 * MAX1721X_REG_MFG_NUMB);
if (get_string(info, MAX1721X_REG_DEV_STR,
@ -403,15 +403,15 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl)
switch (dev_name & MAX172XX_DEV_MASK) {
case MAX172X1_DEV:
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211,
strscpy(info->DeviceName, DEF_DEV_NAME_MAX17211,
2 * MAX1721X_REG_DEV_NUMB);
break;
case MAX172X5_DEV:
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215,
strscpy(info->DeviceName, DEF_DEV_NAME_MAX17215,
2 * MAX1721X_REG_DEV_NUMB);
break;
default:
strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN,
strscpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN,
2 * MAX1721X_REG_DEV_NUMB);
}
}

View file

@ -141,7 +141,7 @@ static int max77650_charger_enable(struct max77650_charger_data *chg)
return rv;
}
static int max77650_charger_disable(struct max77650_charger_data *chg)
static void max77650_charger_disable(struct max77650_charger_data *chg)
{
int rv;
@ -151,8 +151,6 @@ static int max77650_charger_disable(struct max77650_charger_data *chg)
MAX77650_CHARGER_DISABLED);
if (rv)
dev_err(chg->dev, "unable to disable the charger: %d\n", rv);
return rv;
}
static irqreturn_t max77650_charger_check_status(int irq, void *data)
@ -351,7 +349,9 @@ static int max77650_charger_remove(struct platform_device *pdev)
{
struct max77650_charger_data *chg = platform_get_drvdata(pdev);
return max77650_charger_disable(chg);
max77650_charger_disable(chg);
return 0;
}
static const struct of_device_id max77650_charger_of_match[] = {

View file

@ -296,7 +296,7 @@ static ssize_t fast_charge_timer_show(struct device *dev,
break;
}
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
return sysfs_emit(buf, "%u\n", val);
}
static int max77693_set_fast_charge_timer(struct max77693_charger *chg,
@ -357,7 +357,7 @@ static ssize_t top_off_threshold_current_show(struct device *dev,
else
val = data * 50000;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
return sysfs_emit(buf, "%u\n", val);
}
static int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
@ -405,7 +405,7 @@ static ssize_t top_off_timer_show(struct device *dev,
val = data * 10;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
return sysfs_emit(buf, "%u\n", val);
}
static int max77693_set_top_off_timer(struct max77693_charger *chg,

View file

@ -519,7 +519,7 @@ static ssize_t batt_impedance_compensation_show(struct device *dev,
return ret;
rval = (rval >> 4) * 10;
return sprintf(buf, "%d mohm\n", rval);
return sysfs_emit(buf, "%d mohm\n", rval);
}
static ssize_t batt_impedance_compensation_store(struct device *dev,

View file

@ -568,7 +568,7 @@ static ssize_t olpc_bat_error_read(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ec_byte);
return sysfs_emit(buf, "%d\n", ec_byte);
}
static struct device_attribute olpc_bat_error = {

View file

@ -153,7 +153,7 @@ show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
return sprintf(buf, "%d\n", chgmod);
return sysfs_emit(buf, "%d\n", chgmod);
}
static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
@ -174,7 +174,7 @@ show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
else
ma = 0;
return sprintf(buf, "%u\n", ma);
return sysfs_emit(buf, "%u\n", ma);
}
static ssize_t set_usblim(struct device *dev,
@ -207,7 +207,7 @@ show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
ma = (mbc->pcf->pdata->charger_reference_current_ma * mbcc5) >> 8;
return sprintf(buf, "%u\n", ma);
return sysfs_emit(buf, "%u\n", ma);
}
static ssize_t set_chglim(struct device *dev,

View file

@ -1186,83 +1186,6 @@ static void psy_unregister_thermal(struct power_supply *psy)
thermal_zone_device_unregister(psy->tzd);
}
/* thermal cooling device callbacks */
static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long *state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
ret = power_supply_get_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
if (ret)
return ret;
*state = val.intval;
return ret;
}
static int ps_get_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long *state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
ret = power_supply_get_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
if (ret)
return ret;
*state = val.intval;
return ret;
}
static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
val.intval = state;
ret = psy->desc->set_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
return ret;
}
static const struct thermal_cooling_device_ops psy_tcd_ops = {
.get_max_state = ps_get_max_charge_cntl_limit,
.get_cur_state = ps_get_cur_charge_cntl_limit,
.set_cur_state = ps_set_cur_charge_cntl_limit,
};
static int psy_register_cooler(struct power_supply *psy)
{
/* Register for cooling device if psy can control charging */
if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT)) {
psy->tcd = thermal_cooling_device_register(
(char *)psy->desc->name,
psy, &psy_tcd_ops);
return PTR_ERR_OR_ZERO(psy->tcd);
}
return 0;
}
static void psy_unregister_cooler(struct power_supply *psy)
{
if (IS_ERR_OR_NULL(psy->tcd))
return;
thermal_cooling_device_unregister(psy->tcd);
}
#else
static int psy_register_thermal(struct power_supply *psy)
{
@ -1272,15 +1195,6 @@ static int psy_register_thermal(struct power_supply *psy)
static void psy_unregister_thermal(struct power_supply *psy)
{
}
static int psy_register_cooler(struct power_supply *psy)
{
return 0;
}
static void psy_unregister_cooler(struct power_supply *psy)
{
}
#endif
static struct power_supply *__must_check
@ -1354,10 +1268,6 @@ __power_supply_register(struct device *parent,
if (rc)
goto register_thermal_failed;
rc = psy_register_cooler(psy);
if (rc)
goto register_cooler_failed;
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
@ -1387,8 +1297,6 @@ __power_supply_register(struct device *parent,
add_hwmon_sysfs_failed:
power_supply_remove_triggers(psy);
create_triggers_failed:
psy_unregister_cooler(psy);
register_cooler_failed:
psy_unregister_thermal(psy);
register_thermal_failed:
wakeup_init_failed:
@ -1540,7 +1448,6 @@ void power_supply_unregister(struct power_supply *psy)
sysfs_remove_link(&psy->dev.kobj, "powers");
power_supply_remove_hwmon_sysfs(psy);
power_supply_remove_triggers(psy);
psy_unregister_cooler(psy);
psy_unregister_thermal(psy);
device_init_wakeup(&psy->dev, false);
device_unregister(&psy->dev);

View file

@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include "power_supply.h"

View file

@ -249,11 +249,11 @@ static ssize_t power_supply_show_usb_type(struct device *dev,
usb_type = desc->usb_types[i];
if (value->intval == usb_type) {
count += sprintf(buf + count, "[%s] ",
count += sysfs_emit_at(buf, count, "[%s] ",
POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
match = true;
} else {
count += sprintf(buf + count, "%s ",
count += sysfs_emit_at(buf, count, "%s ",
POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
}
}
@ -297,7 +297,7 @@ static ssize_t power_supply_show_property(struct device *dev,
if (ps_attr->text_values_len > 0 &&
value.intval < ps_attr->text_values_len && value.intval >= 0) {
return sprintf(buf, "%s\n", ps_attr->text_values[value.intval]);
return sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]);
}
switch (psp) {
@ -306,10 +306,10 @@ static ssize_t power_supply_show_property(struct device *dev,
&value, buf);
break;
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = sprintf(buf, "%s\n", value.strval);
ret = sysfs_emit(buf, "%s\n", value.strval);
break;
default:
ret = sprintf(buf, "%d\n", value.intval);
ret = sysfs_emit(buf, "%d\n", value.intval);
}
return ret;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,930 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 Richtek Technology Corp.
*
* Authors: Alina Yu <alina_yu@richtek.com>
* ChiYuan Huang <cy_huang@richtek.com>
*/
#include <linux/bits.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kstrtox.h>
#include <linux/linear_range.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/sysfs.h>
#define RT9471_REG_OTGCFG 0x00
#define RT9471_REG_TOP 0x01
#define RT9471_REG_FUNC 0x02
#define RT9471_REG_IBUS 0x03
#define RT9471_REG_VBUS 0x04
#define RT9471_REG_PRECHG 0x05
#define RT9471_REG_VCHG 0x07
#define RT9471_REG_ICHG 0x08
#define RT9471_REG_CHGTMR 0x09
#define RT9471_REG_EOC 0x0A
#define RT9471_REG_INFO 0x0B
#define RT9471_REG_JEITA 0x0C
#define RT9471_REG_PUMP_EXP 0x0D
#define RT9471_REG_DPDMDET 0x0E
#define RT9471_REG_ICSTAT 0x0F
#define RT9471_REG_STAT0 0x10
#define RT9471_REG_STAT1 0x11
#define RT9471_REG_STAT2 0x12
#define RT9471_REG_IRQ0 0x20
#define RT9471_REG_MASK0 0x30
#define RT9471_OTGCV_MASK GENMASK(7, 6)
#define RT9471_OTGCC_MASK BIT(0)
#define RT9471_OTGEN_MASK BIT(1)
#define RT9471_CHGFAULT_MASK GENMASK(4, 1)
#define RT9471_NUM_IRQ_REGS 4
#define RT9471_OTGCV_MINUV 4850000
#define RT9471_OTGCV_STEPUV 150000
#define RT9471_NUM_VOTG 4
#define RT9471_VCHG_MAXUV 4700000
#define RT9471_ICHG_MAXUA 3150000
/* Device ID */
#define RT9470_DEVID 0x09
#define RT9470D_DEVID 0x0A
#define RT9471_DEVID 0x0D
#define RT9471D_DEVID 0x0E
/* IRQ number */
#define RT9471_IRQ_BC12_DONE 0
#define RT9471_IRQ_DETACH 1
#define RT9471_IRQ_RECHG 2
#define RT9471_IRQ_CHG_DONE 3
#define RT9471_IRQ_BG_CHG 4
#define RT9471_IRQ_IE0C 5
#define RT9471_IRQ_CHG_RDY 6
#define RT9471_IRQ_VBUS_GD 7
#define RT9471_IRQ_CHG_BATOV 9
#define RT9471_IRQ_CHG_SYSOV 10
#define RT9471_IRQ_CHG_TOUT 11
#define RT9471_IRQ_CHG_BUSUV 12
#define RT9471_IRQ_CHG_THREG 13
#define RT9471_IRQ_CHG_AICR 14
#define RT9471_IRQ_CHG_MIVR 15
#define RT9471_IRQ_SYS_SHORT 16
#define RT9471_IRQ_SYS_MIN 17
#define RT9471_IRQ_AICC_DONE 18
#define RT9471_IRQ_PE_DONE 19
#define RT9471_IRQ_JEITA_COLD 20
#define RT9471_IRQ_JEITA_COOL 21
#define RT9471_IRQ_JEITA_WARM 22
#define RT9471_IRQ_JEITA_HOT 23
#define RT9471_IRQ_OTG_FAULT 24
#define RT9471_IRQ_OTG_LBP 25
#define RT9471_IRQ_OTG_CC 26
#define RT9471_IRQ_WDT 29
#define RT9471_IRQ_VAC_OV 30
#define RT9471_IRQ_OTP 31
enum rt9471_fields {
F_WDT = 0,
F_WDT_RST,
F_CHG_EN,
F_HZ,
F_BATFET_DIS,
F_AICR,
F_AICC_EN,
F_MIVR,
F_IPRE_CHG,
F_VPRE_CHG,
F_VBAT_REG,
F_ICHG_REG,
F_EOC_RST,
F_TE,
F_IEOC_CHG,
F_DEVICE_ID,
F_REG_RST,
F_BC12_EN,
F_IC_STAT,
F_PORT_STAT,
F_ST_CHG_DONE,
F_ST_CHG_RDY,
F_ST_VBUS_GD,
F_MAX_FIELDS
};
enum rt9471_ranges {
RT9471_RANGE_AICR = 0,
RT9471_RANGE_MIVR,
RT9471_RANGE_IPRE,
RT9471_RANGE_VCHG,
RT9471_RANGE_ICHG,
RT9471_RANGE_IEOC,
RT9471_MAX_RANGES
};
enum {
RT9471_PORTSTAT_APPLE_10W = 8,
RT9471_PORTSTAT_SAMSUNG_10W,
RT9471_PORTSTAT_APPLE_5W,
RT9471_PORTSTAT_APPLE_12W,
RT9471_PORTSTAT_NSTD,
RT9471_PORTSTAT_SDP,
RT9471_PORTSTAT_CDP,
RT9471_PORTSTAT_DCP,
};
struct rt9471_chip {
struct device *dev;
struct regmap *regmap;
struct regmap_field *rm_fields[F_MAX_FIELDS];
struct regmap_irq_chip_data *irq_chip_data;
struct regulator_dev *otg_rdev;
struct power_supply *psy;
struct power_supply_desc psy_desc;
struct mutex var_lock;
enum power_supply_usb_type psy_usb_type;
int psy_usb_curr;
};
static const struct reg_field rt9471_reg_fields[F_MAX_FIELDS] = {
[F_WDT] = REG_FIELD(RT9471_REG_TOP, 0, 0),
[F_WDT_RST] = REG_FIELD(RT9471_REG_TOP, 1, 1),
[F_CHG_EN] = REG_FIELD(RT9471_REG_FUNC, 0, 0),
[F_HZ] = REG_FIELD(RT9471_REG_FUNC, 5, 5),
[F_BATFET_DIS] = REG_FIELD(RT9471_REG_FUNC, 7, 7),
[F_AICR] = REG_FIELD(RT9471_REG_IBUS, 0, 5),
[F_AICC_EN] = REG_FIELD(RT9471_REG_IBUS, 7, 7),
[F_MIVR] = REG_FIELD(RT9471_REG_VBUS, 0, 3),
[F_IPRE_CHG] = REG_FIELD(RT9471_REG_PRECHG, 0, 3),
[F_VPRE_CHG] = REG_FIELD(RT9471_REG_PRECHG, 4, 6),
[F_VBAT_REG] = REG_FIELD(RT9471_REG_VCHG, 0, 6),
[F_ICHG_REG] = REG_FIELD(RT9471_REG_ICHG, 0, 5),
[F_EOC_RST] = REG_FIELD(RT9471_REG_EOC, 0, 0),
[F_TE] = REG_FIELD(RT9471_REG_EOC, 1, 1),
[F_IEOC_CHG] = REG_FIELD(RT9471_REG_EOC, 4, 7),
[F_DEVICE_ID] = REG_FIELD(RT9471_REG_INFO, 3, 6),
[F_REG_RST] = REG_FIELD(RT9471_REG_INFO, 7, 7),
[F_BC12_EN] = REG_FIELD(RT9471_REG_DPDMDET, 7, 7),
[F_IC_STAT] = REG_FIELD(RT9471_REG_ICSTAT, 0, 3),
[F_PORT_STAT] = REG_FIELD(RT9471_REG_ICSTAT, 4, 7),
[F_ST_CHG_DONE] = REG_FIELD(RT9471_REG_STAT0, 3, 3),
[F_ST_CHG_RDY] = REG_FIELD(RT9471_REG_STAT0, 6, 6),
[F_ST_VBUS_GD] = REG_FIELD(RT9471_REG_STAT0, 7, 7),
};
static const struct linear_range rt9471_chg_ranges[RT9471_MAX_RANGES] = {
[RT9471_RANGE_AICR] = { .min = 50000, .min_sel = 1, .max_sel = 63, .step = 50000 },
[RT9471_RANGE_MIVR] = { .min = 3900000, .min_sel = 0, .max_sel = 15, .step = 100000 },
[RT9471_RANGE_IPRE] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 },
[RT9471_RANGE_VCHG] = { .min = 3900000, .min_sel = 0, .max_sel = 80, .step = 10000 },
[RT9471_RANGE_ICHG] = { .min = 0, .min_sel = 0, .max_sel = 63, .step = 50000 },
[RT9471_RANGE_IEOC] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 },
};
static int rt9471_set_value_by_field_range(struct rt9471_chip *chip,
enum rt9471_fields field,
enum rt9471_ranges range, int val)
{
unsigned int sel;
if (val < 0)
return -EINVAL;
linear_range_get_selector_within(rt9471_chg_ranges + range, val, &sel);
return regmap_field_write(chip->rm_fields[field], sel);
}
static int rt9471_get_value_by_field_range(struct rt9471_chip *chip,
enum rt9471_fields field,
enum rt9471_ranges range, int *val)
{
unsigned int sel, rvalue;
int ret;
ret = regmap_field_read(chip->rm_fields[field], &sel);
if (ret)
return ret;
ret = linear_range_get_value(rt9471_chg_ranges + range, sel, &rvalue);
if (ret)
return ret;
*val = rvalue;
return 0;
}
static int rt9471_set_ieoc(struct rt9471_chip *chip, int microamp)
{
int ret;
if (microamp == 0)
return regmap_field_write(chip->rm_fields[F_TE], 0);
ret = rt9471_set_value_by_field_range(chip, F_IEOC_CHG, RT9471_RANGE_IEOC, microamp);
if (ret)
return ret;
/* After applying the new IEOC value, enable charge termination */
return regmap_field_write(chip->rm_fields[F_TE], 1);
}
static int rt9471_get_ieoc(struct rt9471_chip *chip, int *microamp)
{
unsigned int chg_term_enable;
int ret;
ret = regmap_field_read(chip->rm_fields[F_TE], &chg_term_enable);
if (ret)
return ret;
if (!chg_term_enable) {
*microamp = 0;
return 0;
}
return rt9471_get_value_by_field_range(chip, F_IEOC_CHG, RT9471_RANGE_IEOC, microamp);
}
static int rt9471_get_status(struct rt9471_chip *chip, int *status)
{
unsigned int chg_ready, chg_done, fault_stat;
int ret;
ret = regmap_field_read(chip->rm_fields[F_ST_CHG_RDY], &chg_ready);
if (ret)
return ret;
ret = regmap_field_read(chip->rm_fields[F_ST_CHG_DONE], &chg_done);
if (ret)
return ret;
ret = regmap_read(chip->regmap, RT9471_REG_STAT1, &fault_stat);
if (ret)
return ret;
fault_stat &= RT9471_CHGFAULT_MASK;
if (chg_ready && chg_done)
*status = POWER_SUPPLY_STATUS_FULL;
else if (chg_ready && fault_stat)
*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
else if (chg_ready && !fault_stat)
*status = POWER_SUPPLY_STATUS_CHARGING;
else
*status = POWER_SUPPLY_STATUS_DISCHARGING;
return 0;
}
static int rt9471_get_vbus_good(struct rt9471_chip *chip, int *stat)
{
unsigned int vbus_gd;
int ret;
ret = regmap_field_read(chip->rm_fields[F_ST_VBUS_GD], &vbus_gd);
if (ret)
return ret;
*stat = vbus_gd;
return 0;
}
static int rt9471_get_usb_type(struct rt9471_chip *chip, int *usb_type)
{
mutex_lock(&chip->var_lock);
*usb_type = chip->psy_usb_type;
mutex_unlock(&chip->var_lock);
return 0;
}
static int rt9471_get_usb_type_current(struct rt9471_chip *chip,
int *microamp)
{
mutex_lock(&chip->var_lock);
*microamp = chip->psy_usb_curr;
mutex_unlock(&chip->var_lock);
return 0;
}
static enum power_supply_property rt9471_charger_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_usb_type rt9471_charger_usb_types[] = {
POWER_SUPPLY_USB_TYPE_UNKNOWN,
POWER_SUPPLY_USB_TYPE_SDP,
POWER_SUPPLY_USB_TYPE_DCP,
POWER_SUPPLY_USB_TYPE_CDP,
POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID,
};
static int rt9471_charger_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return 1;
default:
return 0;
}
}
static int rt9471_charger_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct rt9471_chip *chip = power_supply_get_drvdata(psy);
int value = val->intval;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
return regmap_field_write(chip->rm_fields[F_CHG_EN], !!value);
case POWER_SUPPLY_PROP_ONLINE:
return regmap_field_write(chip->rm_fields[F_HZ], !value);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return rt9471_set_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, value);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
return rt9471_set_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, value);
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return rt9471_set_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, value);
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
return rt9471_set_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, value);
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
return rt9471_set_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, value);
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return rt9471_set_ieoc(chip, val->intval);
default:
return -EINVAL;
}
}
static const char * const rt9471_manufacturer = "Richtek Technology Corp.";
static const char * const rt9471_model = "RT9471";
static int rt9471_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct rt9471_chip *chip = power_supply_get_drvdata(psy);
int *pvalue = &val->intval;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
return rt9471_get_status(chip, pvalue);
case POWER_SUPPLY_PROP_ONLINE:
return rt9471_get_vbus_good(chip, pvalue);
case POWER_SUPPLY_PROP_CURRENT_MAX:
return rt9471_get_usb_type_current(chip, pvalue);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return rt9471_get_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, pvalue);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
*pvalue = RT9471_ICHG_MAXUA;
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
return rt9471_get_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, pvalue);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
val->intval = RT9471_VCHG_MAXUV;
return 0;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return rt9471_get_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, pvalue);
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
return rt9471_get_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, pvalue);
case POWER_SUPPLY_PROP_USB_TYPE:
return rt9471_get_usb_type(chip, pvalue);
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
return rt9471_get_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, pvalue);
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return rt9471_get_ieoc(chip, pvalue);
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = rt9471_model;
return 0;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = rt9471_manufacturer;
return 0;
default:
return -ENODATA;
}
}
static irqreturn_t rt9471_vbus_gd_handler(int irqno, void *devid)
{
struct rt9471_chip *chip = devid;
power_supply_changed(chip->psy);
return IRQ_HANDLED;
}
static irqreturn_t rt9471_detach_handler(int irqno, void *devid)
{
struct rt9471_chip *chip = devid;
unsigned int vbus_gd;
int ret;
ret = regmap_field_read(chip->rm_fields[F_ST_VBUS_GD], &vbus_gd);
if (ret)
return IRQ_NONE;
/* Only focus on really detached */
if (vbus_gd)
return IRQ_HANDLED;
mutex_lock(&chip->var_lock);
chip->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
chip->psy_usb_curr = 0;
mutex_unlock(&chip->var_lock);
power_supply_changed(chip->psy);
return IRQ_HANDLED;
}
static irqreturn_t rt9471_bc12_done_handler(int irqno, void *devid)
{
struct rt9471_chip *chip = devid;
enum power_supply_usb_type usb_type;
unsigned int port_stat;
int usb_curr, ret;
ret = regmap_field_read(chip->rm_fields[F_PORT_STAT], &port_stat);
if (ret)
return IRQ_NONE;
switch (port_stat) {
case RT9471_PORTSTAT_APPLE_10W:
usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID;
usb_curr = 2000000;
break;
case RT9471_PORTSTAT_APPLE_5W:
usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID;
usb_curr = 1000000;
break;
case RT9471_PORTSTAT_APPLE_12W:
usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID;
usb_curr = 2400000;
break;
case RT9471_PORTSTAT_SAMSUNG_10W:
usb_type = POWER_SUPPLY_USB_TYPE_DCP;
usb_curr = 2000000;
break;
case RT9471_PORTSTAT_DCP:
usb_type = POWER_SUPPLY_USB_TYPE_DCP;
usb_curr = 1500000;
break;
case RT9471_PORTSTAT_NSTD:
case RT9471_PORTSTAT_SDP:
usb_type = POWER_SUPPLY_USB_TYPE_SDP;
usb_curr = 500000;
break;
case RT9471_PORTSTAT_CDP:
usb_type = POWER_SUPPLY_USB_TYPE_CDP;
usb_curr = 1500000;
break;
default:
usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
usb_curr = 0;
break;
}
mutex_lock(&chip->var_lock);
chip->psy_usb_type = usb_type;
chip->psy_usb_curr = usb_curr;
mutex_unlock(&chip->var_lock);
power_supply_changed(chip->psy);
return IRQ_HANDLED;
}
static irqreturn_t rt9471_wdt_handler(int irqno, void *devid)
{
struct rt9471_chip *chip = devid;
int ret;
ret = regmap_field_write(chip->rm_fields[F_WDT_RST], 1);
return ret ? IRQ_NONE : IRQ_HANDLED;
}
static irqreturn_t rt9471_otg_fault_handler(int irqno, void *devid)
{
struct rt9471_chip *chip = devid;
regulator_notifier_call_chain(chip->otg_rdev, REGULATOR_EVENT_FAIL, NULL);
return IRQ_HANDLED;
}
#define RT9471_IRQ_DESC(_name, _hwirq) \
{ \
.name = #_name, \
.hwirq = _hwirq, \
.handler = rt9471_##_name##_handler, \
}
static int rt9471_register_interrupts(struct rt9471_chip *chip)
{
struct device *dev = chip->dev;
static const struct {
char *name;
int hwirq;
irq_handler_t handler;
} chg_irqs[] = {
RT9471_IRQ_DESC(vbus_gd, RT9471_IRQ_VBUS_GD),
RT9471_IRQ_DESC(detach, RT9471_IRQ_DETACH),
RT9471_IRQ_DESC(bc12_done, RT9471_IRQ_BC12_DONE),
RT9471_IRQ_DESC(wdt, RT9471_IRQ_WDT),
RT9471_IRQ_DESC(otg_fault, RT9471_IRQ_OTG_FAULT),
}, *curr;
int i, virq, ret;
for (i = 0; i < ARRAY_SIZE(chg_irqs); i++) {
curr = chg_irqs + i;
virq = regmap_irq_get_virq(chip->irq_chip_data, curr->hwirq);
if (virq <= 0)
return virq;
ret = devm_request_threaded_irq(dev, virq, NULL, curr->handler,
IRQF_ONESHOT, curr->name, chip);
if (ret)
return dev_err_probe(dev, ret, "Failed to register IRQ (%s)\n",
curr->name);
}
return 0;
}
static const struct regulator_ops rt9471_otg_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_current_limit = regulator_set_current_limit_regmap,
.get_current_limit = regulator_get_current_limit_regmap,
};
static const unsigned int rt9471_otg_microamp[] = { 500000, 1200000, };
static const struct regulator_desc rt9471_otg_rdesc = {
.of_match = of_match_ptr("usb-otg-vbus-regulator"),
.name = "rt9471-otg-vbus",
.owner = THIS_MODULE,
.type = REGULATOR_VOLTAGE,
.ops = &rt9471_otg_ops,
.min_uV = RT9471_OTGCV_MINUV,
.uV_step = RT9471_OTGCV_STEPUV,
.n_voltages = RT9471_NUM_VOTG,
.curr_table = rt9471_otg_microamp,
.n_current_limits = ARRAY_SIZE(rt9471_otg_microamp),
.enable_mask = RT9471_OTGEN_MASK,
.enable_reg = RT9471_REG_FUNC,
.vsel_reg = RT9471_REG_OTGCFG,
.vsel_mask = RT9471_OTGCV_MASK,
.csel_reg = RT9471_REG_OTGCFG,
.csel_mask = RT9471_OTGCC_MASK,
};
static int rt9471_register_otg_regulator(struct rt9471_chip *chip)
{
struct device *dev = chip->dev;
struct regulator_config cfg = { .dev = dev, .driver_data = chip };
chip->otg_rdev = devm_regulator_register(dev, &rt9471_otg_rdesc, &cfg);
return PTR_ERR_OR_ZERO(chip->otg_rdev);
}
static inline struct rt9471_chip *psy_device_to_chip(struct device *dev)
{
return power_supply_get_drvdata(to_power_supply(dev));
}
static ssize_t sysoff_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rt9471_chip *chip = psy_device_to_chip(dev);
unsigned int sysoff_enable;
int ret;
ret = regmap_field_read(chip->rm_fields[F_BATFET_DIS], &sysoff_enable);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", sysoff_enable);
}
static ssize_t sysoff_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct rt9471_chip *chip = psy_device_to_chip(dev);
unsigned int tmp;
int ret;
ret = kstrtouint(buf, 10, &tmp);
if (ret)
return ret;
ret = regmap_field_write(chip->rm_fields[F_BATFET_DIS], !!tmp);
if (ret)
return ret;
return count;
}
static ssize_t port_detect_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rt9471_chip *chip = psy_device_to_chip(dev);
unsigned int bc12_enable;
int ret;
ret = regmap_field_read(chip->rm_fields[F_BC12_EN], &bc12_enable);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", bc12_enable);
}
static ssize_t port_detect_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct rt9471_chip *chip = psy_device_to_chip(dev);
unsigned int tmp;
int ret;
ret = kstrtouint(buf, 10, &tmp);
if (ret)
return ret;
ret = regmap_field_write(chip->rm_fields[F_BC12_EN], !!tmp);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(sysoff_enable);
static DEVICE_ATTR_RW(port_detect_enable);
static struct attribute *rt9471_sysfs_attrs[] = {
&dev_attr_sysoff_enable.attr,
&dev_attr_port_detect_enable.attr,
NULL
};
ATTRIBUTE_GROUPS(rt9471_sysfs);
static int rt9471_register_psy(struct rt9471_chip *chip)
{
struct device *dev = chip->dev;
struct power_supply_desc *desc = &chip->psy_desc;
struct power_supply_config cfg = {};
char *psy_name;
cfg.drv_data = chip;
cfg.of_node = dev->of_node;
cfg.attr_grp = rt9471_sysfs_groups;
psy_name = devm_kasprintf(dev, GFP_KERNEL, "rt9471-%s", dev_name(dev));
if (!psy_name)
return -ENOMEM;
desc->name = psy_name;
desc->type = POWER_SUPPLY_TYPE_USB;
desc->usb_types = rt9471_charger_usb_types;
desc->num_usb_types = ARRAY_SIZE(rt9471_charger_usb_types);
desc->properties = rt9471_charger_properties;
desc->num_properties = ARRAY_SIZE(rt9471_charger_properties);
desc->get_property = rt9471_charger_get_property;
desc->set_property = rt9471_charger_set_property;
desc->property_is_writeable = rt9471_charger_property_is_writeable;
chip->psy = devm_power_supply_register(dev, desc, &cfg);
return PTR_ERR_OR_ZERO(chip->psy);
}
static const struct regmap_irq rt9471_regmap_irqs[] = {
REGMAP_IRQ_REG_LINE(RT9471_IRQ_BC12_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_DETACH, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_RECHG, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_BG_CHG, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_IE0C, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_RDY, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_VBUS_GD, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_BATOV, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_SYSOV, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_TOUT, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_BUSUV, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_THREG, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_AICR, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_CHG_MIVR, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_SYS_SHORT, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_SYS_MIN, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_AICC_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_PE_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_COLD, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_COOL, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_WARM, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_JEITA_HOT, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_FAULT, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_LBP, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTG_CC, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_WDT, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_VAC_OV, 8),
REGMAP_IRQ_REG_LINE(RT9471_IRQ_OTP, 8),
};
static const struct regmap_irq_chip rt9471_irq_chip = {
.name = "rt9471-irqs",
.status_base = RT9471_REG_IRQ0,
.mask_base = RT9471_REG_MASK0,
.num_regs = RT9471_NUM_IRQ_REGS,
.irqs = rt9471_regmap_irqs,
.num_irqs = ARRAY_SIZE(rt9471_regmap_irqs),
};
static const struct reg_sequence rt9471_init_regs[] = {
REG_SEQ0(RT9471_REG_INFO, 0x80), /* REG_RST */
REG_SEQ0(RT9471_REG_TOP, 0xC0), /* WDT = 0 */
REG_SEQ0(RT9471_REG_FUNC, 0x01), /* BATFET_DIS_DLY = 0 */
REG_SEQ0(RT9471_REG_IBUS, 0x0A), /* AUTO_AICR = 0 */
REG_SEQ0(RT9471_REG_VBUS, 0xC6), /* VAC_OVP = 14V */
REG_SEQ0(RT9471_REG_JEITA, 0x38), /* JEITA = 0 */
REG_SEQ0(RT9471_REG_DPDMDET, 0x31), /* BC12_EN = 0, DCP_DP_OPT = 1 */
};
static int rt9471_check_devinfo(struct rt9471_chip *chip)
{
struct device *dev = chip->dev;
unsigned int dev_id;
int ret;
ret = regmap_field_read(chip->rm_fields[F_DEVICE_ID], &dev_id);
if (ret)
return dev_err_probe(dev, ret, "Failed to read device_id\n");
switch (dev_id) {
case RT9470_DEVID:
case RT9470D_DEVID:
case RT9471_DEVID:
case RT9471D_DEVID:
return 0;
default:
return dev_err_probe(dev, -ENODEV, "Incorrect device id\n");
}
}
static bool rt9471_accessible_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x00 ... 0x0F:
case 0x10 ... 0x13:
case 0x20 ... 0x33:
case 0x40 ... 0xA1:
return true;
default:
return false;
}
}
static const struct regmap_config rt9471_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xA1,
.writeable_reg = rt9471_accessible_reg,
.readable_reg = rt9471_accessible_reg,
};
static int rt9471_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct rt9471_chip *chip;
struct gpio_desc *ce_gpio;
struct regmap *regmap;
int ret;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->dev = dev;
mutex_init(&chip->var_lock);
i2c_set_clientdata(i2c, chip);
/* Default pull charge enable gpio to make 'CHG_EN' by SW control only */
ce_gpio = devm_gpiod_get_optional(dev, "charge-enable", GPIOD_OUT_HIGH);
if (IS_ERR(ce_gpio))
return dev_err_probe(dev, PTR_ERR(ce_gpio),
"Failed to config charge enable gpio\n");
regmap = devm_regmap_init_i2c(i2c, &rt9471_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
chip->regmap = regmap;
ret = devm_regmap_field_bulk_alloc(dev, regmap, chip->rm_fields,
rt9471_reg_fields,
ARRAY_SIZE(rt9471_reg_fields));
if (ret)
return dev_err_probe(dev, ret, "Failed to alloc regmap field\n");
ret = rt9471_check_devinfo(chip);
if (ret)
return ret;
ret = regmap_register_patch(regmap, rt9471_init_regs,
ARRAY_SIZE(rt9471_init_regs));
if (ret)
return dev_err_probe(dev, ret, "Failed to init registers\n");
ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
&rt9471_irq_chip, &chip->irq_chip_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
ret = rt9471_register_psy(chip);
if (ret)
return dev_err_probe(dev, ret, "Failed to register psy\n");
ret = rt9471_register_otg_regulator(chip);
if (ret)
return dev_err_probe(dev, ret, "Failed to register otg\n");
ret = rt9471_register_interrupts(chip);
if (ret)
return ret;
/* After IRQs are all initialized, enable port detection by default */
return regmap_field_write(chip->rm_fields[F_BC12_EN], 1);
}
static void rt9471_shutdown(struct i2c_client *i2c)
{
struct rt9471_chip *chip = i2c_get_clientdata(i2c);
/*
* There's no external reset pin. Do register reset to guarantee charger
* function is normal after shutdown
*/
regmap_field_write(chip->rm_fields[F_REG_RST], 1);
}
static const struct of_device_id rt9471_of_device_id[] = {
{ .compatible = "richtek,rt9471" },
{}
};
MODULE_DEVICE_TABLE(of, rt9471_of_device_id);
static struct i2c_driver rt9471_driver = {
.driver = {
.name = "rt9471",
.of_match_table = rt9471_of_device_id,
},
.probe_new = rt9471_probe,
.shutdown = rt9471_shutdown,
};
module_i2c_driver(rt9471_driver);
MODULE_DESCRIPTION("Richtek RT9471 charger driver");
MODULE_AUTHOR("Alina Yu <alina_yu@richtek.com>");
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_LICENSE("GPL");

View file

@ -306,8 +306,7 @@ static int map_get_value(struct battery_property_map *map, const char *key,
char buf[MAX_KEYLENGTH];
int cr;
strncpy(buf, key, MAX_KEYLENGTH);
buf[MAX_KEYLENGTH-1] = '\0';
strscpy(buf, key, MAX_KEYLENGTH);
cr = strnlen(buf, MAX_KEYLENGTH) - 1;
if (cr < 0)

View file

@ -726,11 +726,9 @@ twl4030_bci_mode_show(struct device *dev,
for (i = 0; i < ARRAY_SIZE(modes); i++)
if (mode == i)
len += scnprintf(buf+len, PAGE_SIZE-len,
"[%s] ", modes[i]);
len += sysfs_emit_at(buf, len, "[%s] ", modes[i]);
else
len += scnprintf(buf+len, PAGE_SIZE-len,
"%s ", modes[i]);
len += sysfs_emit_at(buf, len, "%s ", modes[i]);
buf[len-1] = '\n';
return len;
}

View file

@ -176,7 +176,7 @@ static ssize_t charger_state_show(struct device *dev,
return 0;
}
return sprintf(buf, "%s\n", charge);
return sysfs_emit(buf, "%s\n", charge);
}
static DEVICE_ATTR_RO(charger_state);