Commit 39f01344 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 90ddb3f0 c142872e
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
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
......@@ -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:
......
......@@ -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
......
# 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>;
};
};
};
# 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>;
};
};
};
......@@ -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),
......
......@@ -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
......
......@@ -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
......
// 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");
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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,
......
......@@ -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);
......
......@@ -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,
......
......@@ -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;
......
......@@ -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;
ret = bq25890_set_otg_cfg(bq, 0);
if (ret)
return ret;
return bq25890_set_otg_cfg(bq, 0);
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);
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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);
}
}
......
......@@ -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[] = {
......
......@@ -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,
......
......@@ -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,
......
......@@ -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 = {
......
......@@ -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,
......
......@@ -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);
......
......@@ -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"
......
......@@ -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;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 Richtek Technology Corp.
*
* Author: ChiYuan Huang <cy_huang@richtek.com>
* ChiaEn Wu <chiaen_wu@richtek.com>
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kstrtox.h>
#include <linux/linear_range.h>
#include <linux/module.h>
#include <linux/mod_devicetable.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/units.h>
#include <linux/sysfs.h>
#define RT9467_REG_CORE_CTRL0 0x00
#define RT9467_REG_CHG_CTRL1 0x01
#define RT9467_REG_CHG_CTRL2 0x02
#define RT9467_REG_CHG_CTRL3 0x03
#define RT9467_REG_CHG_CTRL4 0x04
#define RT9467_REG_CHG_CTRL5 0x05
#define RT9467_REG_CHG_CTRL6 0x06
#define RT9467_REG_CHG_CTRL7 0x07
#define RT9467_REG_CHG_CTRL8 0x08
#define RT9467_REG_CHG_CTRL9 0x09
#define RT9467_REG_CHG_CTRL10 0x0A
#define RT9467_REG_CHG_CTRL12 0x0C
#define RT9467_REG_CHG_CTRL13 0x0D
#define RT9467_REG_CHG_CTRL14 0x0E
#define RT9467_REG_CHG_ADC 0x11
#define RT9467_REG_CHG_DPDM1 0x12
#define RT9467_REG_CHG_DPDM2 0x13
#define RT9467_REG_DEVICE_ID 0x40
#define RT9467_REG_CHG_STAT 0x42
#define RT9467_REG_ADC_DATA_H 0x44
#define RT9467_REG_CHG_STATC 0x50
#define RT9467_REG_CHG_IRQ1 0x53
#define RT9467_REG_CHG_STATC_CTRL 0x60
#define RT9467_REG_CHG_IRQ1_CTRL 0x63
#define RT9467_MASK_PWR_RDY BIT(7)
#define RT9467_MASK_MIVR_STAT BIT(6)
#define RT9467_MASK_OTG_CSEL GENMASK(2, 0)
#define RT9467_MASK_OTG_VSEL GENMASK(7, 2)
#define RT9467_MASK_OTG_EN BIT(0)
#define RT9467_MASK_ADC_IN_SEL GENMASK(7, 4)
#define RT9467_MASK_ADC_START BIT(0)
#define RT9467_NUM_IRQ_REGS 4
#define RT9467_ICHG_MIN_uA 100000
#define RT9467_ICHG_MAX_uA 5000000
#define RT9467_CV_MAX_uV 4710000
#define RT9467_OTG_MIN_uV 4425000
#define RT9467_OTG_MAX_uV 5825000
#define RT9467_OTG_STEP_uV 25000
#define RT9467_NUM_VOTG (RT9467_OTG_MAX_uV - RT9467_OTG_MIN_uV + 1)
#define RT9467_AICLVTH_GAP_uV 200000
#define RT9467_ADCCONV_TIME_MS 35
#define RT9466_VID 0x8
#define RT9467_VID 0x9
/* IRQ number */
#define RT9467_IRQ_TS_STATC 0
#define RT9467_IRQ_CHG_FAULT 1
#define RT9467_IRQ_CHG_STATC 2
#define RT9467_IRQ_CHG_TMR 3
#define RT9467_IRQ_CHG_BATABS 4
#define RT9467_IRQ_CHG_ADPBAD 5
#define RT9467_IRQ_CHG_RVP 6
#define RT9467_IRQ_OTP 7
#define RT9467_IRQ_CHG_AICLM 8
#define RT9467_IRQ_CHG_ICHGM 9
#define RT9467_IRQ_WDTMR 11
#define RT9467_IRQ_SSFINISH 12
#define RT9467_IRQ_CHG_RECHG 13
#define RT9467_IRQ_CHG_TERM 14
#define RT9467_IRQ_CHG_IEOC 15
#define RT9467_IRQ_ADC_DONE 16
#define RT9467_IRQ_PUMPX_DONE 17
#define RT9467_IRQ_BST_BATUV 21
#define RT9467_IRQ_BST_MIDOV 22
#define RT9467_IRQ_BST_OLP 23
#define RT9467_IRQ_ATTACH 24
#define RT9467_IRQ_DETACH 25
#define RT9467_IRQ_HVDCP_DET 29
#define RT9467_IRQ_CHGDET 30
#define RT9467_IRQ_DCDT 31
enum rt9467_fields {
/* RT9467_REG_CORE_CTRL0 */
F_RST = 0,
/* RT9467_REG_CHG_CTRL1 */
F_HZ, F_OTG_PIN_EN, F_OPA_MODE,
/* RT9467_REG_CHG_CTRL2 */
F_SHIP_MODE, F_TE, F_IINLMTSEL, F_CFO_EN, F_CHG_EN,
/* RT9467_REG_CHG_CTRL3 */
F_IAICR, F_ILIM_EN,
/* RT9467_REG_CHG_CTRL4 */
F_VOREG,
/* RT9467_REG_CHG_CTRL6 */
F_VMIVR,
/* RT9467_REG_CHG_CTRL7 */
F_ICHG,
/* RT9467_REG_CHG_CTRL8 */
F_IPREC,
/* RT9467_REG_CHG_CTRL9 */
F_IEOC,
/* RT9467_REG_CHG_CTRL12 */
F_WT_FC,
/* RT9467_REG_CHG_CTRL13 */
F_OCP,
/* RT9467_REG_CHG_CTRL14 */
F_AICL_MEAS, F_AICL_VTH,
/* RT9467_REG_CHG_DPDM1 */
F_USBCHGEN,
/* RT9467_REG_CHG_DPDM2 */
F_USB_STATUS,
/* RT9467_REG_DEVICE_ID */
F_VENDOR,
/* RT9467_REG_CHG_STAT */
F_CHG_STAT,
/* RT9467_REG_CHG_STATC */
F_PWR_RDY, F_CHG_MIVR,
F_MAX_FIELDS
};
static const struct regmap_irq rt9467_irqs[] = {
REGMAP_IRQ_REG_LINE(RT9467_IRQ_TS_STATC, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_FAULT, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_STATC, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_TMR, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_BATABS, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_ADPBAD, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_RVP, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_OTP, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_AICLM, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_ICHGM, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_WDTMR, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_SSFINISH, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_RECHG, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_TERM, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHG_IEOC, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_ADC_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_PUMPX_DONE, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_BATUV, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_MIDOV, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_BST_OLP, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_ATTACH, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_DETACH, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_HVDCP_DET, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_CHGDET, 8),
REGMAP_IRQ_REG_LINE(RT9467_IRQ_DCDT, 8)
};
static const struct regmap_irq_chip rt9467_irq_chip = {
.name = "rt9467-irqs",
.status_base = RT9467_REG_CHG_IRQ1,
.mask_base = RT9467_REG_CHG_IRQ1_CTRL,
.num_regs = RT9467_NUM_IRQ_REGS,
.irqs = rt9467_irqs,
.num_irqs = ARRAY_SIZE(rt9467_irqs),
};
enum rt9467_ranges {
RT9467_RANGE_IAICR = 0,
RT9467_RANGE_VOREG,
RT9467_RANGE_VMIVR,
RT9467_RANGE_ICHG,
RT9467_RANGE_IPREC,
RT9467_RANGE_IEOC,
RT9467_RANGE_AICL_VTH,
RT9467_RANGES_MAX
};
static const struct linear_range rt9467_ranges[RT9467_RANGES_MAX] = {
LINEAR_RANGE_IDX(RT9467_RANGE_IAICR, 100000, 0x0, 0x3F, 50000),
LINEAR_RANGE_IDX(RT9467_RANGE_VOREG, 3900000, 0x0, 0x51, 10000),
LINEAR_RANGE_IDX(RT9467_RANGE_VMIVR, 3900000, 0x0, 0x5F, 100000),
LINEAR_RANGE_IDX(RT9467_RANGE_ICHG, 900000, 0x08, 0x31, 100000),
LINEAR_RANGE_IDX(RT9467_RANGE_IPREC, 100000, 0x0, 0x0F, 50000),
LINEAR_RANGE_IDX(RT9467_RANGE_IEOC, 100000, 0x0, 0x0F, 50000),
LINEAR_RANGE_IDX(RT9467_RANGE_AICL_VTH, 4100000, 0x0, 0x7, 100000),
};
static const struct reg_field rt9467_chg_fields[] = {
[F_RST] = REG_FIELD(RT9467_REG_CORE_CTRL0, 7, 7),
[F_HZ] = REG_FIELD(RT9467_REG_CHG_CTRL1, 2, 2),
[F_OTG_PIN_EN] = REG_FIELD(RT9467_REG_CHG_CTRL1, 1, 1),
[F_OPA_MODE] = REG_FIELD(RT9467_REG_CHG_CTRL1, 0, 0),
[F_SHIP_MODE] = REG_FIELD(RT9467_REG_CHG_CTRL2, 7, 7),
[F_TE] = REG_FIELD(RT9467_REG_CHG_CTRL2, 4, 4),
[F_IINLMTSEL] = REG_FIELD(RT9467_REG_CHG_CTRL2, 2, 3),
[F_CFO_EN] = REG_FIELD(RT9467_REG_CHG_CTRL2, 1, 1),
[F_CHG_EN] = REG_FIELD(RT9467_REG_CHG_CTRL2, 0, 0),
[F_IAICR] = REG_FIELD(RT9467_REG_CHG_CTRL3, 2, 7),
[F_ILIM_EN] = REG_FIELD(RT9467_REG_CHG_CTRL3, 0, 0),
[F_VOREG] = REG_FIELD(RT9467_REG_CHG_CTRL4, 1, 7),
[F_VMIVR] = REG_FIELD(RT9467_REG_CHG_CTRL6, 1, 7),
[F_ICHG] = REG_FIELD(RT9467_REG_CHG_CTRL7, 2, 7),
[F_IPREC] = REG_FIELD(RT9467_REG_CHG_CTRL8, 0, 3),
[F_IEOC] = REG_FIELD(RT9467_REG_CHG_CTRL9, 4, 7),
[F_WT_FC] = REG_FIELD(RT9467_REG_CHG_CTRL12, 5, 7),
[F_OCP] = REG_FIELD(RT9467_REG_CHG_CTRL13, 2, 2),
[F_AICL_MEAS] = REG_FIELD(RT9467_REG_CHG_CTRL14, 7, 7),
[F_AICL_VTH] = REG_FIELD(RT9467_REG_CHG_CTRL14, 0, 2),
[F_USBCHGEN] = REG_FIELD(RT9467_REG_CHG_DPDM1, 7, 7),
[F_USB_STATUS] = REG_FIELD(RT9467_REG_CHG_DPDM2, 0, 2),
[F_VENDOR] = REG_FIELD(RT9467_REG_DEVICE_ID, 4, 7),
[F_CHG_STAT] = REG_FIELD(RT9467_REG_CHG_STAT, 6, 7),
[F_PWR_RDY] = REG_FIELD(RT9467_REG_CHG_STATC, 7, 7),
[F_CHG_MIVR] = REG_FIELD(RT9467_REG_CHG_STATC, 6, 6),
};
enum {
RT9467_STAT_READY = 0,
RT9467_STAT_PROGRESS,
RT9467_STAT_CHARGE_DONE,
RT9467_STAT_FAULT
};
enum rt9467_adc_chan {
RT9467_ADC_VBUS_DIV5 = 0,
RT9467_ADC_VBUS_DIV2,
RT9467_ADC_VSYS,
RT9467_ADC_VBAT,
RT9467_ADC_TS_BAT,
RT9467_ADC_IBUS,
RT9467_ADC_IBAT,
RT9467_ADC_REGN,
RT9467_ADC_TEMP_JC
};
enum rt9467_chg_type {
RT9467_CHG_TYPE_NOVBUS = 0,
RT9467_CHG_TYPE_UNDER_GOING,
RT9467_CHG_TYPE_SDP,
RT9467_CHG_TYPE_SDPNSTD,
RT9467_CHG_TYPE_DCP,
RT9467_CHG_TYPE_CDP,
RT9467_CHG_TYPE_MAX
};
enum rt9467_iin_limit_sel {
RT9467_IINLMTSEL_3_2A = 0,
RT9467_IINLMTSEL_CHG_TYP,
RT9467_IINLMTSEL_AICR,
RT9467_IINLMTSEL_LOWER_LEVEL, /* lower of above three */
};
struct rt9467_chg_data {
struct device *dev;
struct regmap *regmap;
struct regmap_field *rm_field[F_MAX_FIELDS];
struct regmap_irq_chip_data *irq_chip_data;
struct power_supply *psy;
struct mutex adc_lock;
struct mutex attach_lock;
struct mutex ichg_ieoc_lock;
struct regulator_dev *rdev;
struct completion aicl_done;
enum power_supply_usb_type psy_usb_type;
unsigned int old_stat;
unsigned int vid;
int ichg_ua;
int ieoc_ua;
};
static int rt9467_otg_of_parse_cb(struct device_node *of,
const struct regulator_desc *desc,
struct regulator_config *cfg)
{
struct rt9467_chg_data *data = cfg->driver_data;
cfg->ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(of),
"enable", 0, GPIOD_OUT_LOW |
GPIOD_FLAGS_BIT_NONEXCLUSIVE,
desc->name);
if (IS_ERR(cfg->ena_gpiod)) {
cfg->ena_gpiod = NULL;
return 0;
}
return regmap_field_write(data->rm_field[F_OTG_PIN_EN], 1);
}
static const struct regulator_ops rt9467_otg_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_current_limit = regulator_set_current_limit_regmap,
.get_current_limit = regulator_get_current_limit_regmap,
};
static const u32 rt9467_otg_microamp[] = {
500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000
};
static const struct regulator_desc rt9467_otg_desc = {
.name = "rt9476-usb-otg-vbus",
.of_match = "usb-otg-vbus-regulator",
.of_parse_cb = rt9467_otg_of_parse_cb,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.min_uV = RT9467_OTG_MIN_uV,
.uV_step = RT9467_OTG_STEP_uV,
.n_voltages = RT9467_NUM_VOTG,
.curr_table = rt9467_otg_microamp,
.n_current_limits = ARRAY_SIZE(rt9467_otg_microamp),
.csel_reg = RT9467_REG_CHG_CTRL10,
.csel_mask = RT9467_MASK_OTG_CSEL,
.vsel_reg = RT9467_REG_CHG_CTRL5,
.vsel_mask = RT9467_MASK_OTG_VSEL,
.enable_reg = RT9467_REG_CHG_CTRL1,
.enable_mask = RT9467_MASK_OTG_EN,
.ops = &rt9467_otg_regulator_ops,
};
static int rt9467_register_otg_regulator(struct rt9467_chg_data *data)
{
struct regulator_config cfg = {
.dev = data->dev,
.regmap = data->regmap,
.driver_data = data,
};
data->rdev = devm_regulator_register(data->dev, &rt9467_otg_desc, &cfg);
return PTR_ERR_OR_ZERO(data->rdev);
}
static int rt9467_get_value_from_ranges(struct rt9467_chg_data *data,
enum rt9467_fields field,
enum rt9467_ranges rsel,
int *value)
{
const struct linear_range *range = rt9467_ranges + rsel;
unsigned int sel;
int ret;
ret = regmap_field_read(data->rm_field[field], &sel);
if (ret)
return ret;
return linear_range_get_value(range, sel, value);
}
static int rt9467_set_value_from_ranges(struct rt9467_chg_data *data,
enum rt9467_fields field,
enum rt9467_ranges rsel,
int value)
{
const struct linear_range *range = rt9467_ranges + rsel;
unsigned int sel;
bool found;
int ret;
if (rsel == RT9467_RANGE_VMIVR) {
ret = linear_range_get_selector_high(range, value, &sel, &found);
if (ret)
value = range->max_sel;
} else {
linear_range_get_selector_within(range, value, &sel);
}
return regmap_field_write(data->rm_field[field], sel);
}
static int rt9467_get_adc_sel(enum rt9467_adc_chan chan, int *sel)
{
switch (chan) {
case RT9467_ADC_VBUS_DIV5:
case RT9467_ADC_VBUS_DIV2:
case RT9467_ADC_VSYS:
case RT9467_ADC_VBAT:
*sel = chan + 1;
return 0;
case RT9467_ADC_TS_BAT:
*sel = chan + 2;
return 0;
case RT9467_ADC_IBUS:
case RT9467_ADC_IBAT:
*sel = chan + 3;
return 0;
case RT9467_ADC_REGN:
case RT9467_ADC_TEMP_JC:
*sel = chan + 4;
return 0;
default:
return -EINVAL;
}
}
static int rt9467_get_adc_raw_data(struct rt9467_chg_data *data,
enum rt9467_adc_chan chan, int *val)
{
unsigned int adc_stat, reg_val, adc_sel;
__be16 chan_raw_data;
int ret;
mutex_lock(&data->adc_lock);
ret = rt9467_get_adc_sel(chan, &adc_sel);
if (ret)
goto adc_unlock;
ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, 0);
if (ret) {
dev_err(data->dev, "Failed to clear ADC enable\n");
goto adc_unlock;
}
reg_val = RT9467_MASK_ADC_START | FIELD_PREP(RT9467_MASK_ADC_IN_SEL, adc_sel);
ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, reg_val);
if (ret)
goto adc_unlock;
/* Minimum wait time for one channel processing */
msleep(RT9467_ADCCONV_TIME_MS);
ret = regmap_read_poll_timeout(data->regmap, RT9467_REG_CHG_ADC,
adc_stat,
!(adc_stat & RT9467_MASK_ADC_START),
MILLI, RT9467_ADCCONV_TIME_MS * MILLI);
if (ret) {
dev_err(data->dev, "Failed to wait ADC conversion, chan = %d\n", chan);
goto adc_unlock;
}
ret = regmap_raw_read(data->regmap, RT9467_REG_ADC_DATA_H,
&chan_raw_data, sizeof(chan_raw_data));
if (ret)
goto adc_unlock;
*val = be16_to_cpu(chan_raw_data);
adc_unlock:
mutex_unlock(&data->adc_lock);
return ret;
}
static int rt9467_get_adc(struct rt9467_chg_data *data,
enum rt9467_adc_chan chan, int *val)
{
unsigned int aicr_ua, ichg_ua;
int ret;
ret = rt9467_get_adc_raw_data(data, chan, val);
if (ret)
return ret;
switch (chan) {
case RT9467_ADC_VBUS_DIV5:
*val *= 25000;
return 0;
case RT9467_ADC_VBUS_DIV2:
*val *= 10000;
return 0;
case RT9467_ADC_VBAT:
case RT9467_ADC_VSYS:
case RT9467_ADC_REGN:
*val *= 5000;
return 0;
case RT9467_ADC_TS_BAT:
*val /= 400;
return 0;
case RT9467_ADC_IBUS:
/* UUG MOS turn-on ratio will affect the IBUS adc scale */
ret = rt9467_get_value_from_ranges(data, F_IAICR,
RT9467_RANGE_IAICR, &aicr_ua);
if (ret)
return ret;
*val *= aicr_ua < 400000 ? 29480 : 50000;
return 0;
case RT9467_ADC_IBAT:
/* PP MOS turn-on ratio will affect the ICHG adc scale */
ret = rt9467_get_value_from_ranges(data, F_ICHG,
RT9467_RANGE_ICHG, &ichg_ua);
if (ret)
return ret;
*val *= ichg_ua <= 400000 ? 28500 :
ichg_ua <= 800000 ? 31500 : 500000;
return 0;
case RT9467_ADC_TEMP_JC:
*val = ((*val * 2) - 40) * 10;
return 0;
default:
return -EINVAL;
}
}
static int rt9467_psy_get_status(struct rt9467_chg_data *data, int *state)
{
unsigned int status;
int ret;
ret = regmap_field_read(data->rm_field[F_CHG_STAT], &status);
if (ret)
return ret;
switch (status) {
case RT9467_STAT_READY:
*state = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
case RT9467_STAT_PROGRESS:
*state = POWER_SUPPLY_STATUS_CHARGING;
return 0;
case RT9467_STAT_CHARGE_DONE:
*state = POWER_SUPPLY_STATUS_FULL;
return 0;
default:
*state = POWER_SUPPLY_STATUS_UNKNOWN;
return 0;
}
}
static int rt9467_psy_set_ichg(struct rt9467_chg_data *data, int microamp)
{
int ret;
mutex_lock(&data->ichg_ieoc_lock);
if (microamp < 500000) {
dev_err(data->dev, "Minimum value must be 500mA\n");
microamp = 500000;
}
ret = rt9467_set_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG, microamp);
if (ret)
goto out;
ret = rt9467_get_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG,
&data->ichg_ua);
if (ret)
goto out;
out:
mutex_unlock(&data->ichg_ieoc_lock);
return ret;
}
static int rt9467_run_aicl(struct rt9467_chg_data *data)
{
unsigned int statc, aicl_vth;
int mivr_vth, aicr_get;
int ret = 0;
ret = regmap_read(data->regmap, RT9467_REG_CHG_STATC, &statc);
if (ret) {
dev_err(data->dev, "Failed to read status\n");
return ret;
}
if (!(statc & RT9467_MASK_PWR_RDY) || !(statc & RT9467_MASK_MIVR_STAT)) {
dev_info(data->dev, "Condition not matched %d\n", statc);
return 0;
}
ret = rt9467_get_value_from_ranges(data, F_VMIVR, RT9467_RANGE_VMIVR,
&mivr_vth);
if (ret) {
dev_err(data->dev, "Failed to get mivr\n");
return ret;
}
/* AICL_VTH = MIVR_VTH + 200mV */
aicl_vth = mivr_vth + RT9467_AICLVTH_GAP_uV;
ret = rt9467_set_value_from_ranges(data, F_AICL_VTH,
RT9467_RANGE_AICL_VTH, aicl_vth);
/* Trigger AICL function */
ret = regmap_field_write(data->rm_field[F_AICL_MEAS], 1);
if (ret) {
dev_err(data->dev, "Failed to set aicl measurement\n");
return ret;
}
reinit_completion(&data->aicl_done);
ret = wait_for_completion_timeout(&data->aicl_done, msecs_to_jiffies(3500));
if (ret)
return ret;
ret = rt9467_get_value_from_ranges(data, F_IAICR, RT9467_RANGE_IAICR, &aicr_get);
if (ret) {
dev_err(data->dev, "Failed to get aicr\n");
return ret;
}
dev_info(data->dev, "aicr get = %d uA\n", aicr_get);
return 0;
}
static int rt9467_psy_set_ieoc(struct rt9467_chg_data *data, int microamp)
{
int ret;
mutex_lock(&data->ichg_ieoc_lock);
ret = rt9467_set_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC, microamp);
if (ret)
goto out;
ret = rt9467_get_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC, &data->ieoc_ua);
if (ret)
goto out;
out:
mutex_unlock(&data->ichg_ieoc_lock);
return ret;
}
static const enum power_supply_usb_type rt9467_chg_usb_types[] = {
POWER_SUPPLY_USB_TYPE_UNKNOWN,
POWER_SUPPLY_USB_TYPE_SDP,
POWER_SUPPLY_USB_TYPE_DCP,
POWER_SUPPLY_USB_TYPE_CDP,
};
static const enum power_supply_property rt9467_chg_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,
};
static int rt9467_psy_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct rt9467_chg_data *data = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
return rt9467_psy_get_status(data, &val->intval);
case POWER_SUPPLY_PROP_ONLINE:
return regmap_field_read(data->rm_field[F_PWR_RDY], &val->intval);
case POWER_SUPPLY_PROP_CURRENT_MAX:
mutex_lock(&data->attach_lock);
if (data->psy_usb_type == POWER_SUPPLY_USB_TYPE_UNKNOWN ||
data->psy_usb_type == POWER_SUPPLY_USB_TYPE_SDP)
val->intval = 500000;
else
val->intval = 1500000;
mutex_unlock(&data->attach_lock);
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
mutex_lock(&data->ichg_ieoc_lock);
val->intval = data->ichg_ua;
mutex_unlock(&data->ichg_ieoc_lock);
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = RT9467_ICHG_MAX_uA;
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
return rt9467_get_value_from_ranges(data, F_VOREG,
RT9467_RANGE_VOREG,
&val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
val->intval = RT9467_CV_MAX_uV;
return 0;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return rt9467_get_value_from_ranges(data, F_IAICR,
RT9467_RANGE_IAICR,
&val->intval);
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
return rt9467_get_value_from_ranges(data, F_VMIVR,
RT9467_RANGE_VMIVR,
&val->intval);
case POWER_SUPPLY_PROP_USB_TYPE:
mutex_lock(&data->attach_lock);
val->intval = data->psy_usb_type;
mutex_unlock(&data->attach_lock);
return 0;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
return rt9467_get_value_from_ranges(data, F_IPREC,
RT9467_RANGE_IPREC,
&val->intval);
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
mutex_lock(&data->ichg_ieoc_lock);
val->intval = data->ieoc_ua;
mutex_unlock(&data->ichg_ieoc_lock);
return 0;
default:
return -ENODATA;
}
}
static int rt9467_psy_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct rt9467_chg_data *data = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
return regmap_field_write(data->rm_field[F_CHG_EN], val->intval);
case POWER_SUPPLY_PROP_ONLINE:
return regmap_field_write(data->rm_field[F_HZ], val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return rt9467_psy_set_ichg(data, val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
return rt9467_set_value_from_ranges(data, F_VOREG,
RT9467_RANGE_VOREG, val->intval);
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
if (val->intval == -1)
return rt9467_run_aicl(data);
else
return rt9467_set_value_from_ranges(data, F_IAICR,
RT9467_RANGE_IAICR,
val->intval);
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
return rt9467_set_value_from_ranges(data, F_VMIVR,
RT9467_RANGE_VMIVR, val->intval);
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
return rt9467_set_value_from_ranges(data, F_IPREC,
RT9467_RANGE_IPREC, val->intval);
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return rt9467_psy_set_ieoc(data, val->intval);
case POWER_SUPPLY_PROP_USB_TYPE:
return regmap_field_write(data->rm_field[F_USBCHGEN], val->intval);
default:
return -EINVAL;
}
}
static int rt9467_chg_prop_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_CHARGE_TERM_CURRENT:
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
case POWER_SUPPLY_PROP_USB_TYPE:
return 1;
default:
return 0;
}
}
static const struct power_supply_desc rt9467_chg_psy_desc = {
.name = "rt9467-charger",
.type = POWER_SUPPLY_TYPE_USB,
.usb_types = rt9467_chg_usb_types,
.num_usb_types = ARRAY_SIZE(rt9467_chg_usb_types),
.properties = rt9467_chg_properties,
.num_properties = ARRAY_SIZE(rt9467_chg_properties),
.property_is_writeable = rt9467_chg_prop_is_writeable,
.get_property = rt9467_psy_get_property,
.set_property = rt9467_psy_set_property,
};
static inline struct rt9467_chg_data *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 rt9467_chg_data *data = psy_device_to_chip(dev);
unsigned int sysoff_enable;
int ret;
ret = regmap_field_read(data->rm_field[F_SHIP_MODE], &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 rt9467_chg_data *data = psy_device_to_chip(dev);
unsigned int tmp;
int ret;
ret = kstrtouint(buf, 10, &tmp);
if (ret)
return ret;
ret = regmap_field_write(data->rm_field[F_SHIP_MODE], !!tmp);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(sysoff_enable);
static struct attribute *rt9467_sysfs_attrs[] = {
&dev_attr_sysoff_enable.attr,
NULL
};
ATTRIBUTE_GROUPS(rt9467_sysfs);
static int rt9467_register_psy(struct rt9467_chg_data *data)
{
struct power_supply_config cfg = {
.drv_data = data,
.of_node = dev_of_node(data->dev),
.attr_grp = rt9467_sysfs_groups,
};
data->psy = devm_power_supply_register(data->dev, &rt9467_chg_psy_desc,
&cfg);
return PTR_ERR_OR_ZERO(data->psy);
}
static int rt9467_mivr_handler(struct rt9467_chg_data *data)
{
unsigned int mivr_act;
int ret, ibus_ma;
/*
* back-boost workaround
* If (mivr_active & ibus < 100mA), toggle cfo bit
*/
ret = regmap_field_read(data->rm_field[F_CHG_MIVR], &mivr_act);
if (ret) {
dev_err(data->dev, "Failed to read MIVR stat\n");
return ret;
}
if (!mivr_act)
return 0;
ret = rt9467_get_adc(data, RT9467_ADC_IBUS, &ibus_ma);
if (ret) {
dev_err(data->dev, "Failed to get IBUS\n");
return ret;
}
if (ibus_ma < 100000) {
ret = regmap_field_write(data->rm_field[F_CFO_EN], 0);
ret |= regmap_field_write(data->rm_field[F_CFO_EN], 1);
if (ret)
dev_err(data->dev, "Failed to toggle cfo\n");
}
return ret;
}
static irqreturn_t rt9467_statc_handler(int irq, void *priv)
{
struct rt9467_chg_data *data = priv;
unsigned int new_stat, evts = 0;
int ret;
ret = regmap_read(data->regmap, RT9467_REG_CHG_STATC, &new_stat);
if (ret) {
dev_err(data->dev, "Failed to read chg_statc\n");
return IRQ_NONE;
}
evts = data->old_stat ^ new_stat;
data->old_stat = new_stat;
if ((evts & new_stat) & RT9467_MASK_MIVR_STAT) {
ret = rt9467_mivr_handler(data);
if (ret)
dev_err(data->dev, "Failed to handle mivr stat\n");
}
return IRQ_HANDLED;
}
static irqreturn_t rt9467_wdt_handler(int irq, void *priv)
{
struct rt9467_chg_data *data = priv;
unsigned int dev_id;
int ret;
/* Any i2c communication can kick watchdog timer */
ret = regmap_read(data->regmap, RT9467_REG_DEVICE_ID, &dev_id);
if (ret) {
dev_err(data->dev, "Failed to kick wdt (%d)\n", ret);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static int rt9467_report_usb_state(struct rt9467_chg_data *data)
{
unsigned int usb_stat, power_ready;
bool psy_changed = true;
int ret;
ret = regmap_field_read(data->rm_field[F_USB_STATUS], &usb_stat);
ret |= regmap_field_read(data->rm_field[F_PWR_RDY], &power_ready);
if (ret)
return ret;
if (!power_ready)
usb_stat = RT9467_CHG_TYPE_NOVBUS;
mutex_lock(&data->attach_lock);
switch (usb_stat) {
case RT9467_CHG_TYPE_NOVBUS:
data->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
break;
case RT9467_CHG_TYPE_SDP:
data->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
break;
case RT9467_CHG_TYPE_SDPNSTD:
data->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
break;
case RT9467_CHG_TYPE_DCP:
data->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
break;
case RT9467_CHG_TYPE_CDP:
data->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
break;
case RT9467_CHG_TYPE_UNDER_GOING:
default:
psy_changed = false;
break;
}
mutex_unlock(&data->attach_lock);
if (psy_changed)
power_supply_changed(data->psy);
return 0;
}
static irqreturn_t rt9467_usb_state_handler(int irq, void *priv)
{
struct rt9467_chg_data *data = priv;
int ret;
ret = rt9467_report_usb_state(data);
if (ret) {
dev_err(data->dev, "Failed to report attach type (%d)\n", ret);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static irqreturn_t rt9467_aiclmeas_handler(int irq, void *priv)
{
struct rt9467_chg_data *data = priv;
complete(&data->aicl_done);
return IRQ_HANDLED;
}
#define RT9467_IRQ_DESC(_name, _handler_func, _hwirq) \
{ \
.name = #_name, \
.handler = rt9467_##_handler_func##_handler, \
.hwirq = _hwirq, \
}
static int rt9467_request_interrupt(struct rt9467_chg_data *data)
{
struct device *dev = data->dev;
static const struct {
const char *name;
int hwirq;
irq_handler_t handler;
} rt9467_exclusive_irqs[] = {
RT9467_IRQ_DESC(statc, statc, RT9467_IRQ_TS_STATC),
RT9467_IRQ_DESC(wdt, wdt, RT9467_IRQ_WDTMR),
RT9467_IRQ_DESC(attach, usb_state, RT9467_IRQ_ATTACH),
RT9467_IRQ_DESC(detach, usb_state, RT9467_IRQ_DETACH),
RT9467_IRQ_DESC(aiclmeas, aiclmeas, RT9467_IRQ_CHG_AICLM),
}, rt9466_exclusive_irqs[] = {
RT9467_IRQ_DESC(statc, statc, RT9467_IRQ_TS_STATC),
RT9467_IRQ_DESC(wdt, wdt, RT9467_IRQ_WDTMR),
RT9467_IRQ_DESC(aiclmeas, aiclmeas, RT9467_IRQ_CHG_AICLM),
}, *chg_irqs;
int num_chg_irqs, i, virq, ret;
if (data->vid == RT9466_VID) {
chg_irqs = rt9466_exclusive_irqs;
num_chg_irqs = ARRAY_SIZE(rt9466_exclusive_irqs);
} else {
chg_irqs = rt9467_exclusive_irqs;
num_chg_irqs = ARRAY_SIZE(rt9467_exclusive_irqs);
}
for (i = 0; i < num_chg_irqs; i++) {
virq = regmap_irq_get_virq(data->irq_chip_data, chg_irqs[i].hwirq);
if (virq <= 0)
return dev_err_probe(dev, virq, "Failed to get (%s) irq\n",
chg_irqs[i].name);
ret = devm_request_threaded_irq(dev, virq, NULL, chg_irqs[i].handler,
IRQF_ONESHOT, chg_irqs[i].name, data);
if (ret)
return dev_err_probe(dev, ret, "Failed to request (%s) irq\n",
chg_irqs[i].name);
}
return 0;
}
static int rt9467_do_charger_init(struct rt9467_chg_data *data)
{
struct device *dev = data->dev;
int ret;
ret = regmap_write(data->regmap, RT9467_REG_CHG_ADC, 0);
if (ret)
return dev_err_probe(dev, ret, "Failed to reset ADC\n");
ret = rt9467_get_value_from_ranges(data, F_ICHG, RT9467_RANGE_ICHG,
&data->ichg_ua);
ret |= rt9467_get_value_from_ranges(data, F_IEOC, RT9467_RANGE_IEOC,
&data->ieoc_ua);
if (ret)
return dev_err_probe(dev, ret, "Failed to init ichg/ieoc value\n");
ret = regmap_update_bits(data->regmap, RT9467_REG_CHG_STATC_CTRL,
RT9467_MASK_PWR_RDY | RT9467_MASK_MIVR_STAT, 0);
if (ret)
return dev_err_probe(dev, ret, "Failed to make statc unmask\n");
/* Select IINLMTSEL to use AICR */
ret = regmap_field_write(data->rm_field[F_IINLMTSEL],
RT9467_IINLMTSEL_AICR);
if (ret)
return dev_err_probe(dev, ret, "Failed to set iinlmtsel to AICR\n");
/* Wait for AICR Rampping */
msleep(150);
/* Disable hardware ILIM */
ret = regmap_field_write(data->rm_field[F_ILIM_EN], 0);
if (ret)
return dev_err_probe(dev, ret, "Failed to disable hardware ILIM\n");
/* Set inductor OCP to high level */
ret = regmap_field_write(data->rm_field[F_OCP], 1);
if (ret)
return dev_err_probe(dev, ret, "Failed to set higher inductor OCP level\n");
/* Set charge termination default enable */
ret = regmap_field_write(data->rm_field[F_TE], 1);
if (ret)
return dev_err_probe(dev, ret, "Failed to set TE=1\n");
/* Set 12hrs fast charger timer */
ret = regmap_field_write(data->rm_field[F_WT_FC], 4);
if (ret)
return dev_err_probe(dev, ret, "Failed to set WT_FC\n");
/* Toggle BC12 function */
ret = regmap_field_write(data->rm_field[F_USBCHGEN], 0);
if (ret)
return dev_err_probe(dev, ret, "Failed to disable BC12\n");
return regmap_field_write(data->rm_field[F_USBCHGEN], 1);
}
static bool rt9467_is_accessible_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x00 ... 0x1A:
case 0x20 ... 0x38:
case 0x40 ... 0x49:
case 0x50 ... 0x57:
case 0x60 ... 0x67:
case 0x70 ... 0x79:
case 0x82 ... 0x85:
return true;
default:
return false;
}
}
static const struct regmap_config rt9467_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x85,
.writeable_reg = rt9467_is_accessible_reg,
.readable_reg = rt9467_is_accessible_reg,
};
static int rt9467_check_vendor_info(struct rt9467_chg_data *data)
{
unsigned int vid;
int ret;
ret = regmap_field_read(data->rm_field[F_VENDOR], &vid);
if (ret) {
dev_err(data->dev, "Failed to get vid\n");
return ret;
}
if ((vid != RT9466_VID) && (vid != RT9467_VID))
return dev_err_probe(data->dev, -ENODEV,
"VID not correct [0x%02X]\n", vid);
data->vid = vid;
return 0;
}
static int rt9467_reset_chip(struct rt9467_chg_data *data)
{
int ret;
/* Disable HZ before reset chip */
ret = regmap_field_write(data->rm_field[F_HZ], 0);
if (ret)
return ret;
return regmap_field_write(data->rm_field[F_RST], 1);
}
static void rt9467_chg_destroy_adc_lock(void *data)
{
struct mutex *adc_lock = data;
mutex_destroy(adc_lock);
}
static void rt9467_chg_destroy_attach_lock(void *data)
{
struct mutex *attach_lock = data;
mutex_destroy(attach_lock);
}
static void rt9467_chg_destroy_ichg_ieoc_lock(void *data)
{
struct mutex *ichg_ieoc_lock = data;
mutex_destroy(ichg_ieoc_lock);
}
static void rt9467_chg_complete_aicl_done(void *data)
{
struct completion *aicl_done = data;
complete(aicl_done);
}
static int rt9467_charger_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct rt9467_chg_data *data;
struct gpio_desc *ceb_gpio;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = &i2c->dev;
i2c_set_clientdata(i2c, data);
/* Default pull charge enable gpio to make 'CHG_EN' by SW control only */
ceb_gpio = devm_gpiod_get_optional(dev, "charge-enable", GPIOD_OUT_LOW);
if (IS_ERR(ceb_gpio))
return dev_err_probe(dev, PTR_ERR(ceb_gpio),
"Failed to config charge enable gpio\n");
data->regmap = devm_regmap_init_i2c(i2c, &rt9467_regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap),
"Failed to init regmap\n");
ret = devm_regmap_field_bulk_alloc(dev, data->regmap,
data->rm_field, rt9467_chg_fields,
ARRAY_SIZE(rt9467_chg_fields));
if (ret)
return dev_err_probe(dev, ret, "Failed to alloc regmap fields\n");
ret = rt9467_check_vendor_info(data);
if (ret)
return dev_err_probe(dev, ret, "Failed to check vendor info");
ret = rt9467_reset_chip(data);
if (ret)
return dev_err_probe(dev, ret, "Failed to reset chip\n");
ret = devm_regmap_add_irq_chip(dev, data->regmap, i2c->irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, 0,
&rt9467_irq_chip, &data->irq_chip_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to add irq chip\n");
mutex_init(&data->adc_lock);
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_adc_lock,
&data->adc_lock);
if (ret)
return dev_err_probe(dev, ret, "Failed to init ADC lock\n");
mutex_init(&data->attach_lock);
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_attach_lock,
&data->attach_lock);
if (ret)
return dev_err_probe(dev, ret, "Failed to init attach lock\n");
mutex_init(&data->ichg_ieoc_lock);
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_ichg_ieoc_lock,
&data->ichg_ieoc_lock);
if (ret)
return dev_err_probe(dev, ret, "Failed to init ICHG/IEOC lock\n");
init_completion(&data->aicl_done);
ret = devm_add_action_or_reset(dev, rt9467_chg_complete_aicl_done,
&data->aicl_done);
if (ret)
return dev_err_probe(dev, ret, "Failed to init AICL done completion\n");
ret = rt9467_do_charger_init(data);
if (ret)
return ret;
ret = rt9467_register_otg_regulator(data);
if (ret)
return ret;
ret = rt9467_register_psy(data);
if (ret)
return ret;
return rt9467_request_interrupt(data);
}
static const struct of_device_id rt9467_charger_of_match_table[] = {
{ .compatible = "richtek,rt9467", },
{}
};
MODULE_DEVICE_TABLE(of, rt9467_charger_of_match_table);
static struct i2c_driver rt9467_charger_driver = {
.driver = {
.name = "rt9467-charger",
.of_match_table = rt9467_charger_of_match_table,
},
.probe_new = rt9467_charger_probe,
};
module_i2c_driver(rt9467_charger_driver);
MODULE_DESCRIPTION("Richtek RT9467 Charger Driver");
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>");
MODULE_LICENSE("GPL");
// 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");
......@@ -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)
......
......@@ -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;
}
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment