power: supply: Import xiaomi power supply drivers from yudi-t-oss
Converted to LF and ran clang-format Change-Id: I04b75fbbaab9949b67dc7222c17af045bc21e98d Signed-off-by: Jens Reidel <adrian@travitia.xyz>
This commit is contained in:
parent
05e7d49733
commit
680c1731f7
@ -895,6 +895,13 @@ config MI_CHARGER_M81
|
||||
Say Y here to enable xiaomi properties similar to
|
||||
qti_battery_charger_main_m81 module on stock
|
||||
|
||||
config XM_POWER_SUPPLY
|
||||
tristate "Xiaomi power supply config"
|
||||
help
|
||||
Say Y or M here to enable xiaomi power supply
|
||||
|
||||
source "drivers/power/supply/qcom/Kconfig"
|
||||
|
||||
source "drivers/power/supply/xiaomi/Kconfig"
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
@ -103,3 +103,4 @@ obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o
|
||||
obj-$(CONFIG_SCHGM_FLASH) += schgm-flash.o
|
||||
obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
|
||||
obj-$(CONFIG_QCOM_POWER_SUPPLY) += qcom/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xiaomi/
|
||||
|
36
drivers/power/supply/xiaomi/Kconfig
Normal file
36
drivers/power/supply/xiaomi/Kconfig
Normal file
@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config PD_RT17XX
|
||||
tristate "RT17XX PDPHY"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Say Y to enable support for rt17xx pdphy.
|
||||
|
||||
config CHARGER_SYV690D
|
||||
tristate "SYV690D Charger"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Say Y to enable support for syv690d Charger.
|
||||
|
||||
config FUEL_GAUGE_BQ27Z561
|
||||
tristate "BQ27z561 fuel gauge driver"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Say Y to enable support for bq27z561 fuel gauge driver
|
||||
|
||||
config CHARGER_PUMP_SC8551A
|
||||
tristate "SC8551 Divider Charger Driver"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Say Y to enable support for sc8551a charger pump driver
|
||||
|
||||
config CHARGER_PUMP_LN8000
|
||||
tristate "LN8000 Divider Charger Driver"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Say Y to enable support for ln8000 charger pump driver
|
5
drivers/power/supply/xiaomi/Makefile
Normal file
5
drivers/power/supply/xiaomi/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += battmngr/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += common/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += platform/
|
6
drivers/power/supply/xiaomi/battmngr/Makefile
Normal file
6
drivers/power/supply/xiaomi/battmngr/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += battery/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += battmngr_iio/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += charger/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += init/
|
4
drivers/power/supply/xiaomi/battmngr/battery/Makefile
Normal file
4
drivers/power/supply/xiaomi/battmngr/battery/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xm_battery.o
|
||||
xm_battery-objs := xm_battery_core.o xm_battery_feature.o
|
428
drivers/power/supply/xiaomi/battmngr/battery/xm_battery_core.c
Normal file
428
drivers/power/supply/xiaomi/battmngr/battery/xm_battery_core.c
Normal file
@ -0,0 +1,428 @@
|
||||
|
||||
#include <linux/battmngr/xm_battery_core.h>
|
||||
|
||||
struct xm_battery *g_xm_battery;
|
||||
EXPORT_SYMBOL(g_xm_battery);
|
||||
|
||||
static void batt_check_charger_psy(struct xm_battery *battery)
|
||||
{
|
||||
if (!battery->usb_psy) {
|
||||
battery->usb_psy = power_supply_get_by_name("usb");
|
||||
if (!battery->usb_psy)
|
||||
battery_err("%s usb psy not found!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
int battery_process_event_fg(struct battmngr_notify *noti_data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
battery_err("%s: msg_type %d\n", __func__, noti_data->fg_msg.msg_type);
|
||||
|
||||
switch (noti_data->fg_msg.msg_type) {
|
||||
case BATTMNGR_MSG_FG:
|
||||
cancel_delayed_work(
|
||||
&(g_xm_battery->batt_feature->xm_prop_change_work));
|
||||
g_xm_battery->batt_feature->update_cont = 1;
|
||||
schedule_delayed_work(
|
||||
&(g_xm_battery->batt_feature->xm_prop_change_work), 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(battery_process_event_fg);
|
||||
|
||||
static int get_prop_batt_health(struct xm_battery *battery,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!battery)
|
||||
return -EINVAL;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TEMP,
|
||||
&val->intval);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval >= BATT_OVERHEAT_THRESHOLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else if (val->intval >= BATT_WARM_THRESHOLD &&
|
||||
val->intval < BATT_OVERHEAT_THRESHOLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_WARM;
|
||||
else if (val->intval >= BATT_COOL_THRESHOLD &&
|
||||
val->intval < BATT_WARM_THRESHOLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
else if (val->intval >= BATT_COLD_THRESHOLD &&
|
||||
val->intval < BATT_COOL_THRESHOLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_COOL;
|
||||
else if (val->intval < BATT_COLD_THRESHOLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_COLD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_prop_batt_capacity_level(struct xm_battery *battery,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0, capacity;
|
||||
|
||||
if (!battery)
|
||||
return -EINVAL;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CAPACITY, &capacity);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (capacity == 0)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
||||
else if (capacity > 0 && capacity <= 20)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
||||
else if (capacity > 20 && capacity <= 80)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
||||
else if (capacity > 80 && capacity <= 99)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
|
||||
else if (capacity == 100)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_prop_batt_capacity(struct xm_battery *battery,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!battery)
|
||||
return -EINVAL;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CAPACITY, &val->intval);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval == 0)
|
||||
power_supply_changed(battery->batt_psy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int set_prop_system_temp_level(struct xm_battery *battery,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (val->intval < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval >= MAX_TEMP_LEVEL)
|
||||
return -EINVAL;
|
||||
|
||||
rc = power_supply_set_property(battery->usb_psy, psp, val);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static enum power_supply_property batt_psy_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
};
|
||||
|
||||
static int batt_psy_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *pval)
|
||||
{
|
||||
int rc = 0;
|
||||
int chg_online = 0, chg_present = 0, fg_status = 0;
|
||||
struct xm_battery *battery = power_supply_get_drvdata(psy);
|
||||
|
||||
batt_check_charger_psy(battery);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_PRESENT,
|
||||
&chg_present);
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_ONLINE,
|
||||
&chg_online);
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_STATUS,
|
||||
&pval->intval);
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_STATUS, &fg_status);
|
||||
if (chg_present && chg_online) {
|
||||
if (fg_status == POWER_SUPPLY_STATUS_FULL ||
|
||||
pval->intval == POWER_SUPPLY_STATUS_FULL)
|
||||
pval->intval = POWER_SUPPLY_STATUS_FULL;
|
||||
else if ((pval->intval != POWER_SUPPLY_STATUS_FULL) &&
|
||||
(g_xm_charger->input_suspend == 0))
|
||||
pval->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
rc = get_prop_batt_health(battery, pval);
|
||||
if (rc != 0)
|
||||
pval->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_PRESENT, &pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
if (battery->usb_psy)
|
||||
rc = power_supply_get_property(battery->usb_psy, psp,
|
||||
pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_TYPE,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
||||
rc = get_prop_batt_capacity_level(battery, pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
rc = get_prop_batt_capacity(battery, pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
if (battery->usb_psy)
|
||||
rc = power_supply_get_property(battery->usb_psy, psp,
|
||||
pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
||||
if (battery->usb_psy)
|
||||
rc = power_supply_get_property(battery->usb_psy, psp,
|
||||
pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_VOLTAGE_NOW,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||
if (battery->usb_psy)
|
||||
rc = power_supply_get_property(battery->usb_psy, psp,
|
||||
pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CURRENT_NOW,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
if (battery->usb_psy)
|
||||
rc = power_supply_get_property(
|
||||
battery->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
pval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
pval->intval = get_effective_result(g_xm_charger->fcc_votable);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_TERM,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_TEMP, &pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
pval->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_RM, &pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CYCLE_COUNT,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CHARGE_FULL,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CHARGE_FULL_DESIGN,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_TIME_TO_FULL_NOW,
|
||||
&pval->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_TIME_TO_EMPTY_NOW,
|
||||
&pval->intval);
|
||||
break;
|
||||
default:
|
||||
battery_err("%s: batt power supply prop %d not supported\n",
|
||||
__func__, psp);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
battery_err("%s: Couldn't get prop %d rc = %d\n", __func__, psp,
|
||||
rc);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batt_psy_set_prop(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0;
|
||||
struct xm_battery *battery = power_supply_get_drvdata(psy);
|
||||
|
||||
batt_check_charger_psy(battery);
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
if (battery->usb_psy)
|
||||
rc = set_prop_system_temp_level(battery, prop, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
rc = xm_battmngr_write_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CAPACITY, val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
rc = xm_battmngr_write_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_TEMP, val->intval);
|
||||
break;
|
||||
default:
|
||||
battery_err("%s: batt power supply prop %d not supported\n",
|
||||
__func__, prop);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int batt_psy_prop_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc batt_psy_desc = {
|
||||
.name = "battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = batt_psy_props,
|
||||
.num_properties = ARRAY_SIZE(batt_psy_props),
|
||||
.get_property = batt_psy_get_prop,
|
||||
.set_property = batt_psy_set_prop,
|
||||
.property_is_writeable = batt_psy_prop_is_writeable,
|
||||
};
|
||||
|
||||
static int xm_init_batt_psy(struct xm_battery *battery)
|
||||
{
|
||||
struct power_supply_config batt_cfg = {};
|
||||
int rc = 0;
|
||||
|
||||
batt_cfg.drv_data = battery;
|
||||
batt_cfg.of_node = battery->dev->of_node;
|
||||
battery->batt_psy = devm_power_supply_register(
|
||||
battery->dev, &batt_psy_desc, &batt_cfg);
|
||||
if (IS_ERR(battery->batt_psy)) {
|
||||
battery_err("%s: Couldn't register battery power supply\n",
|
||||
__func__);
|
||||
return PTR_ERR(battery->batt_psy);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xm_battery_parse_dt(struct xm_battery *battery)
|
||||
{
|
||||
struct device_node *node = battery->dev->of_node;
|
||||
|
||||
if (!node) {
|
||||
battery_err("%s: device tree node missing\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int xm_battery_init(struct xm_battery *battery)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
battery_err("%s: Start\n", __func__);
|
||||
|
||||
rc = xm_battery_parse_dt(battery);
|
||||
if (rc < 0) {
|
||||
battery_err("%s: Couldn't parse device tree rc=%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_init_batt_psy(battery);
|
||||
if (rc < 0) {
|
||||
battery_err("%s: Couldn't initialize batt psy rc=%d\n",
|
||||
__func__, rc);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
rc = xm_batt_feature_init(battery);
|
||||
if (rc < 0) {
|
||||
battery_err("%s: Couldn't init xm_batt_feature rc=%d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
battery->usb_psy = power_supply_get_by_name("usb");
|
||||
if (!battery->usb_psy)
|
||||
battery_err("%s get usb psy fail\n", __func__);
|
||||
|
||||
battery_err("%s: End\n", __func__);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_battery_init);
|
||||
|
||||
MODULE_DESCRIPTION("Xiaomi Battery core");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -0,0 +1,94 @@
|
||||
#include <linux/battmngr/xm_battery_core.h>
|
||||
|
||||
static int xm_get_shutdown_delay(void)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_CAPACITY,
|
||||
&val);
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_SHUTDOWN_DELAY, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define MAX_UEVENT_LENGTH 50
|
||||
static void generate_xm_charge_uvent(struct work_struct *work)
|
||||
{
|
||||
int count;
|
||||
struct batt_feature_info *chip = container_of(
|
||||
work, struct batt_feature_info, xm_prop_change_work.work);
|
||||
|
||||
static char uevent_string[][MAX_UEVENT_LENGTH + 1] = {
|
||||
"POWER_SUPPLY_SHUTDOWN_DELAY=\n", //length=28+8
|
||||
};
|
||||
static char *envp[] = {
|
||||
uevent_string[0],
|
||||
NULL,
|
||||
};
|
||||
char *prop_buf = NULL;
|
||||
|
||||
count = chip->update_cont;
|
||||
if (chip->update_cont < 0)
|
||||
return;
|
||||
|
||||
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!prop_buf)
|
||||
return;
|
||||
|
||||
scnprintf(prop_buf, PAGE_SIZE, "%u", xm_get_shutdown_delay());
|
||||
strncpy(uevent_string[0] + 28, prop_buf, MAX_UEVENT_LENGTH - 28);
|
||||
|
||||
battery_err("uevent test : %s, count %d\n", envp[0], count);
|
||||
|
||||
/*add our prop end*/
|
||||
|
||||
kobject_uevent_env(&chip->dev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
free_page((unsigned long)prop_buf);
|
||||
chip->update_cont = count - 1;
|
||||
schedule_delayed_work(&chip->xm_prop_change_work,
|
||||
msecs_to_jiffies(2000));
|
||||
return;
|
||||
}
|
||||
|
||||
int xm_batt_feature_init(struct xm_battery *battery)
|
||||
{
|
||||
struct batt_feature_info *chip;
|
||||
|
||||
battery_err("%s: Start\n", __func__);
|
||||
|
||||
if (battery->batt_feature) {
|
||||
battery_err("%s: Already initialized\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(battery->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
chip->dev = battery->dev;
|
||||
chip->update_cont = 0;
|
||||
|
||||
INIT_DELAYED_WORK(&chip->xm_prop_change_work, generate_xm_charge_uvent);
|
||||
|
||||
schedule_delayed_work(&chip->xm_prop_change_work,
|
||||
msecs_to_jiffies(30000));
|
||||
|
||||
battery->batt_feature = chip;
|
||||
battery_err("%s: End\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xm_batt_feature_deinit(void)
|
||||
{
|
||||
struct batt_feature_info *chip = g_xm_battery->batt_feature;
|
||||
|
||||
if (!chip)
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&chip->xm_prop_change_work);
|
||||
chip = NULL;
|
||||
|
||||
return;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xm_battmngr_iio.o
|
@ -0,0 +1,296 @@
|
||||
|
||||
#include <linux/battmngr/xm_battmngr_iio.h>
|
||||
|
||||
struct xm_battmngr_iio *g_battmngr_iio;
|
||||
EXPORT_SYMBOL(g_battmngr_iio);
|
||||
|
||||
int xm_get_iio_channel(struct xm_battmngr_iio *battmngr_iio,
|
||||
const char *propname, struct iio_channel **chan)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = of_property_match_string(battmngr_iio->dev->of_node,
|
||||
"io-channel-names", propname);
|
||||
if (rc < 0)
|
||||
return 0;
|
||||
|
||||
*chan = devm_iio_channel_get(battmngr_iio->dev, propname);
|
||||
if (IS_ERR(*chan)) {
|
||||
rc = PTR_ERR(*chan);
|
||||
if (rc != -EPROBE_DEFER)
|
||||
pr_err("%s channel unavailable, %d\n", propname, rc);
|
||||
*chan = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_get_iio_channel);
|
||||
|
||||
bool is_cp_master_chan_valid(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum cp_iio_channels chan)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_cp[chan]))
|
||||
return false;
|
||||
|
||||
if (!battmngr_iio->iio_chan_list_cp[chan]) {
|
||||
battmngr_iio->iio_chan_list_cp[chan] = devm_iio_channel_get(
|
||||
battmngr_iio->dev, cp_iio_chan[chan]);
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_cp[chan])) {
|
||||
rc = PTR_ERR(battmngr_iio->iio_chan_list_cp[chan]);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
battmngr_iio->iio_chan_list_cp[chan] = NULL;
|
||||
pr_err("Failed to get IIO channel %s, rc=%d\n",
|
||||
cp_iio_chan[chan], rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(is_cp_master_chan_valid);
|
||||
|
||||
bool is_cp_slave_chan_valid(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum cp_iio_channels chan)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_cp_sec[chan]))
|
||||
return false;
|
||||
|
||||
if (!battmngr_iio->iio_chan_list_cp_sec[chan]) {
|
||||
battmngr_iio->iio_chan_list_cp_sec[chan] = devm_iio_channel_get(
|
||||
battmngr_iio->dev, cp_sec_iio_chan[chan]);
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_cp_sec[chan])) {
|
||||
rc = PTR_ERR(battmngr_iio->iio_chan_list_cp_sec[chan]);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
battmngr_iio->iio_chan_list_cp_sec[chan] = NULL;
|
||||
pr_err("Failed to get IIO channel %s, rc=%d\n",
|
||||
cp_sec_iio_chan[chan], rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(is_cp_slave_chan_valid);
|
||||
|
||||
bool is_main_chg_chan_valid(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum main_chg_iio_channels chan)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_main_chg[chan]))
|
||||
return false;
|
||||
|
||||
if (!battmngr_iio->iio_chan_list_main_chg[chan]) {
|
||||
battmngr_iio->iio_chan_list_main_chg[chan] =
|
||||
devm_iio_channel_get(battmngr_iio->dev,
|
||||
main_chg_iio_chan[chan]);
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_main_chg[chan])) {
|
||||
rc = PTR_ERR(
|
||||
battmngr_iio->iio_chan_list_main_chg[chan]);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
battmngr_iio->iio_chan_list_main_chg[chan] =
|
||||
NULL;
|
||||
pr_err("Failed to get IIO channel %s, rc=%d\n",
|
||||
main_chg_iio_chan[chan], rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(is_main_chg_chan_valid);
|
||||
|
||||
bool is_batt_fg_chan_valid(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum batt_fg_iio_channels chan)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_batt_fg[chan]))
|
||||
return false;
|
||||
|
||||
if (!battmngr_iio->iio_chan_list_batt_fg[chan]) {
|
||||
battmngr_iio->iio_chan_list_batt_fg[chan] =
|
||||
devm_iio_channel_get(battmngr_iio->dev,
|
||||
batt_fg_iio_chan[chan]);
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_batt_fg[chan])) {
|
||||
rc = PTR_ERR(battmngr_iio->iio_chan_list_batt_fg[chan]);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
battmngr_iio->iio_chan_list_batt_fg[chan] =
|
||||
NULL;
|
||||
pr_err("Failed to get IIO channel %s, rc=%d\n",
|
||||
batt_fg_iio_chan[chan], rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(is_batt_fg_chan_valid);
|
||||
|
||||
bool is_pd_chan_valid(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum batt_fg_iio_channels chan)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_pd[chan]))
|
||||
return false;
|
||||
|
||||
if (!battmngr_iio->iio_chan_list_pd[chan]) {
|
||||
battmngr_iio->iio_chan_list_pd[chan] = devm_iio_channel_get(
|
||||
battmngr_iio->dev, pd_iio_chan[chan]);
|
||||
if (IS_ERR(battmngr_iio->iio_chan_list_pd[chan])) {
|
||||
rc = PTR_ERR(battmngr_iio->iio_chan_list_pd[chan]);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
battmngr_iio->iio_chan_list_pd[chan] = NULL;
|
||||
pr_err("Failed to get IIO channel %s, rc=%d\n",
|
||||
pd_iio_chan[chan], rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(is_pd_chan_valid);
|
||||
|
||||
int xm_battmngr_read_iio_prop(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum iio_type type, int iio_chan, int *val)
|
||||
{
|
||||
struct iio_channel *iio_chan_list;
|
||||
int rc;
|
||||
|
||||
if (!battmngr_iio)
|
||||
return -ENODEV;
|
||||
|
||||
switch (type) {
|
||||
case CP_MASTER:
|
||||
if (!is_cp_master_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_cp[iio_chan];
|
||||
break;
|
||||
case CP_SLAVE:
|
||||
if (!is_cp_slave_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_cp_sec[iio_chan];
|
||||
break;
|
||||
case MAIN_CHG:
|
||||
if (!is_main_chg_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_main_chg[iio_chan];
|
||||
break;
|
||||
case BATT_FG:
|
||||
if (!is_batt_fg_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_batt_fg[iio_chan];
|
||||
break;
|
||||
case PD_PHY:
|
||||
if (!is_pd_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_pd[iio_chan];
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("iio_type %d is not supported\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = iio_read_channel_processed(iio_chan_list, val);
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_battmngr_read_iio_prop);
|
||||
|
||||
int xm_battmngr_write_iio_prop(struct xm_battmngr_iio *battmngr_iio,
|
||||
enum iio_type type, int iio_chan, int val)
|
||||
{
|
||||
struct iio_channel *iio_chan_list;
|
||||
int rc;
|
||||
|
||||
if (!battmngr_iio)
|
||||
return -ENODEV;
|
||||
|
||||
switch (type) {
|
||||
case CP_MASTER:
|
||||
if (!is_cp_master_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_cp[iio_chan];
|
||||
break;
|
||||
case CP_SLAVE:
|
||||
if (!is_cp_slave_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_cp_sec[iio_chan];
|
||||
break;
|
||||
case MAIN_CHG:
|
||||
if (!is_main_chg_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_main_chg[iio_chan];
|
||||
break;
|
||||
case BATT_FG:
|
||||
if (!is_batt_fg_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_batt_fg[iio_chan];
|
||||
break;
|
||||
case PD_PHY:
|
||||
if (!is_pd_chan_valid(battmngr_iio, iio_chan))
|
||||
return -ENODEV;
|
||||
iio_chan_list = battmngr_iio->iio_chan_list_pd[iio_chan];
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("iio_type %d is not supported\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = iio_write_channel_raw(iio_chan_list, val);
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_battmngr_write_iio_prop);
|
||||
|
||||
int xm_battmngr_iio_init(struct xm_battmngr_iio *battmngr_iio)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
pr_err("xm_battmngr_iio_init start\n");
|
||||
|
||||
battmngr_iio->iio_chan_list_cp =
|
||||
devm_kcalloc(battmngr_iio->dev, ARRAY_SIZE(cp_iio_chan),
|
||||
sizeof(*battmngr_iio->iio_chan_list_cp),
|
||||
GFP_KERNEL);
|
||||
if (!battmngr_iio->iio_chan_list_cp)
|
||||
return -ENOMEM;
|
||||
|
||||
battmngr_iio->iio_chan_list_cp_sec =
|
||||
devm_kcalloc(battmngr_iio->dev, ARRAY_SIZE(cp_sec_iio_chan),
|
||||
sizeof(*battmngr_iio->iio_chan_list_cp_sec),
|
||||
GFP_KERNEL);
|
||||
if (!battmngr_iio->iio_chan_list_cp_sec)
|
||||
return -ENOMEM;
|
||||
|
||||
battmngr_iio->iio_chan_list_main_chg =
|
||||
devm_kcalloc(battmngr_iio->dev, ARRAY_SIZE(main_chg_iio_chan),
|
||||
sizeof(*battmngr_iio->iio_chan_list_main_chg),
|
||||
GFP_KERNEL);
|
||||
if (!battmngr_iio->iio_chan_list_main_chg)
|
||||
return -ENOMEM;
|
||||
|
||||
battmngr_iio->iio_chan_list_batt_fg =
|
||||
devm_kcalloc(battmngr_iio->dev, ARRAY_SIZE(batt_fg_iio_chan),
|
||||
sizeof(*battmngr_iio->iio_chan_list_batt_fg),
|
||||
GFP_KERNEL);
|
||||
if (!battmngr_iio->iio_chan_list_batt_fg)
|
||||
return -ENOMEM;
|
||||
|
||||
battmngr_iio->iio_chan_list_pd =
|
||||
devm_kcalloc(battmngr_iio->dev, ARRAY_SIZE(pd_iio_chan),
|
||||
sizeof(*battmngr_iio->iio_chan_list_pd),
|
||||
GFP_KERNEL);
|
||||
if (!battmngr_iio->iio_chan_list_pd)
|
||||
return -ENOMEM;
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_battmngr_iio_init);
|
||||
|
||||
MODULE_DESCRIPTION("Xiaomi Battery Manager iio");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
4
drivers/power/supply/xiaomi/battmngr/charger/Makefile
Normal file
4
drivers/power/supply/xiaomi/battmngr/charger/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += main/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += quick_chg/
|
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xm_charger.o
|
||||
xm_charger-objs := xm_charger_core.o xm_charger_votable.o xm_charger_ffc.o \
|
||||
xm_charger_thermal.o step-chg-jeita.o xm_charger_feature.o
|
1012
drivers/power/supply/xiaomi/battmngr/charger/main/step-chg-jeita.c
Normal file
1012
drivers/power/supply/xiaomi/battmngr/charger/main/step-chg-jeita.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,509 @@
|
||||
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
|
||||
struct xm_charger *g_xm_charger;
|
||||
EXPORT_SYMBOL(g_xm_charger);
|
||||
|
||||
static void charger_check_battery_psy(struct xm_charger *charger)
|
||||
{
|
||||
if (!charger->batt_psy) {
|
||||
charger->batt_psy = power_supply_get_by_name("battery");
|
||||
if (!charger->batt_psy)
|
||||
charger_err("%s batt psy not found!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_pd_bc12_active(struct xm_charger *charger,
|
||||
struct battmngr_notify *noti_data)
|
||||
{
|
||||
static int active_flag;
|
||||
charger->pd_active = noti_data->pd_msg.pd_active;
|
||||
if (!charger->pd_active) {
|
||||
charger->pd_verified = 0;
|
||||
g_battmngr_noti->pd_msg.pd_verified = 0;
|
||||
}
|
||||
|
||||
charger->bc12_active = noti_data->mainchg_msg.chg_plugin;
|
||||
|
||||
if (!active_flag && (charger->pd_active || charger->bc12_active)) {
|
||||
vote(charger->awake_votable, CHG_AWAKE_VOTER, true, 0);
|
||||
active_flag = 1;
|
||||
charger_err("%s: chg pm_awake\n", __func__);
|
||||
} else if (active_flag && !charger->pd_active &&
|
||||
!charger->bc12_active) {
|
||||
g_battmngr_noti->misc_msg.disable_thermal = 0;
|
||||
g_battmngr_noti->misc_msg.vindpm_temp = 0;
|
||||
vote(charger->awake_votable, CHG_AWAKE_VOTER, false, 0);
|
||||
active_flag = 0;
|
||||
charger_err("%s: chg pm_relax\n", __func__);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void update_usb_type(struct xm_charger *chg)
|
||||
{
|
||||
static int last_bc12_type;
|
||||
static int last_pd_active;
|
||||
if (chg->pd_active) {
|
||||
usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
|
||||
charger_err("%s: pd_active=%d\n", __func__, chg->pd_active);
|
||||
} else {
|
||||
chg->bc12_type = g_battmngr_noti->mainchg_msg.chg_type;
|
||||
switch (chg->bc12_type) {
|
||||
case POWER_SUPPLY_USB_TYPE_SDP:
|
||||
usb_psy_desc.type = POWER_SUPPLY_TYPE_USB;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_FLOAT:
|
||||
case POWER_SUPPLY_USB_TYPE_DCP:
|
||||
case POWER_SUPPLY_TYPE_USB_HVDCP:
|
||||
usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
|
||||
break;
|
||||
case POWER_SUPPLY_USB_TYPE_CDP:
|
||||
usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
|
||||
break;
|
||||
default:
|
||||
usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
}
|
||||
charger_err("%s: usb_type=%d, usb_psy_desc.type=%d\n", __func__,
|
||||
chg->bc12_type, usb_psy_desc.type);
|
||||
}
|
||||
|
||||
if (last_bc12_type != chg->bc12_type ||
|
||||
last_pd_active != chg->pd_active) {
|
||||
cancel_delayed_work(&(chg->chg_feature->xm_prop_change_work));
|
||||
chg->chg_feature->update_cont = 0;
|
||||
schedule_delayed_work(&(chg->chg_feature->xm_prop_change_work),
|
||||
msecs_to_jiffies(120));
|
||||
}
|
||||
last_bc12_type = chg->bc12_type;
|
||||
last_pd_active = chg->pd_active;
|
||||
chg->real_type = usb_psy_desc.type;
|
||||
power_supply_changed(chg->usb_psy);
|
||||
power_supply_changed(chg->batt_psy);
|
||||
charger_err("%s: usb_type=%d, usb_psy_desc.type=%d\n", __func__,
|
||||
chg->bc12_type, usb_psy_desc.type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int charger_process_event_cp(struct battmngr_notify *noti_data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
charger_err("%s: msg_type %d\n", __func__, noti_data->cp_msg.msg_type);
|
||||
|
||||
switch (noti_data->cp_msg.msg_type) {
|
||||
case BATTMNGR_MSG_CP_MASTER:
|
||||
break;
|
||||
case BATTMNGR_MSG_CP_SLAVE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(charger_process_event_cp);
|
||||
|
||||
int charger_process_event_mainchg(struct battmngr_notify *noti_data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
charger_err("%s: msg_type %d\n", __func__,
|
||||
noti_data->mainchg_msg.msg_type);
|
||||
|
||||
switch (noti_data->mainchg_msg.msg_type) {
|
||||
case BATTMNGR_MSG_MAINCHG_TYPE:
|
||||
update_pd_bc12_active(g_xm_charger, noti_data);
|
||||
update_usb_type(g_xm_charger);
|
||||
typec_conn_therm_start_stop(g_xm_charger,
|
||||
g_xm_charger->chg_feature);
|
||||
stepchg_jeita_start_stop(g_xm_charger, g_xm_charger->step_chg);
|
||||
night_charging_start_stop(g_xm_charger,
|
||||
g_xm_charger->chg_feature);
|
||||
xm_charger_set_fastcharge_mode(g_xm_charger,
|
||||
g_xm_charger->pd_verified);
|
||||
if (g_xm_charger->input_suspend == 1)
|
||||
xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_ENABLED, 0);
|
||||
break;
|
||||
case BATTMNGR_MSG_MAINCHG_OTG_ENABLE:
|
||||
g_xm_charger->otg_enable =
|
||||
g_battmngr_noti->mainchg_msg.otg_enable;
|
||||
typec_conn_therm_start_stop(g_xm_charger,
|
||||
g_xm_charger->chg_feature);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(charger_process_event_mainchg);
|
||||
|
||||
int charger_process_event_pd(struct battmngr_notify *noti_data)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
charger_err("%s: msg_type %d\n", __func__, noti_data->pd_msg.msg_type);
|
||||
|
||||
switch (noti_data->pd_msg.msg_type) {
|
||||
case BATTMNGR_MSG_PD_ACTIVE:
|
||||
update_pd_bc12_active(g_xm_charger, noti_data);
|
||||
update_usb_type(g_xm_charger);
|
||||
typec_conn_therm_start_stop(g_xm_charger,
|
||||
g_xm_charger->chg_feature);
|
||||
stepchg_jeita_start_stop(g_xm_charger, g_xm_charger->step_chg);
|
||||
night_charging_start_stop(g_xm_charger,
|
||||
g_xm_charger->chg_feature);
|
||||
xm_charger_set_fastcharge_mode(g_xm_charger,
|
||||
g_xm_charger->pd_verified);
|
||||
break;
|
||||
case BATTMNGR_MSG_PD_VERIFED:
|
||||
xm_charger_set_fastcharge_mode(g_xm_charger,
|
||||
g_xm_charger->pd_verified);
|
||||
cancel_delayed_work(
|
||||
&(g_xm_charger->chg_feature->xm_prop_change_work));
|
||||
g_xm_charger->chg_feature->update_cont = 0;
|
||||
schedule_delayed_work(
|
||||
&(g_xm_charger->chg_feature->xm_prop_change_work),
|
||||
msecs_to_jiffies(600));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(charger_process_event_pd);
|
||||
|
||||
static int chg_votable_init(struct xm_charger *chg)
|
||||
{
|
||||
chg->fcc_votable = find_votable("FCC");
|
||||
chg->fv_votable = find_votable("FV");
|
||||
chg->usb_icl_votable = find_votable("ICL");
|
||||
|
||||
if (!chg->fcc_votable || !chg->fv_votable || !chg->usb_icl_votable)
|
||||
return -EINVAL;
|
||||
|
||||
vote(chg->fcc_votable, CHG_INIT_VOTER, true, chg->dt.batt_current_max);
|
||||
vote(chg->fv_votable, CHG_INIT_VOTER, true, chg->dt.chg_voltage_max);
|
||||
vote(chg->usb_icl_votable, CHG_INIT_VOTER, true,
|
||||
chg->dt.input_batt_current_max);
|
||||
vote(chg->input_suspend_votable, CHG_INIT_VOTER, false, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property usb_psy_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_TYPE,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
|
||||
};
|
||||
|
||||
static int usb_psy_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0, mode;
|
||||
struct xm_charger *charger = power_supply_get_drvdata(psy);
|
||||
|
||||
charger_check_battery_psy(charger);
|
||||
//update_usb_type(charger);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_PRESENT,
|
||||
&val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_ONLINE,
|
||||
&val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = charger->dt.chg_design_voltage_max;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_FASTCHARGE_MODE, &mode);
|
||||
if (mode)
|
||||
val->intval = charger->dt.chg_voltage_max;
|
||||
else
|
||||
val->intval = charger->dt.non_ffc_cv;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_BUS_VOLTAGE,
|
||||
&val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_CURRENT,
|
||||
&val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
val->intval = charger->dt.batt_current_max;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TYPE:
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
val->intval = charger->dt.input_batt_current_max;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
val->intval = charger->system_temp_level;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
||||
val->intval = charger->thermal_levels;
|
||||
break;
|
||||
default:
|
||||
charger_err("%s: get prop %d is not supported in usb\n",
|
||||
__func__, psp);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get prop %d rc = %d\n\n", __func__,
|
||||
psp, rc);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_psy_set_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
int rc = 0;
|
||||
struct xm_charger *charger = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
charger->system_temp_level = val->intval;
|
||||
rc = xm_charger_thermal(charger);
|
||||
break;
|
||||
default:
|
||||
charger_err("%s: Set prop %d is not supported in usb psy\n",
|
||||
__func__, psp);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int usb_psy_prop_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct power_supply_desc usb_psy_desc = {
|
||||
.name = "usb",
|
||||
.type = POWER_SUPPLY_TYPE_UNKNOWN,
|
||||
.properties = usb_psy_props,
|
||||
.num_properties = ARRAY_SIZE(usb_psy_props),
|
||||
.get_property = usb_psy_get_prop,
|
||||
.set_property = usb_psy_set_prop,
|
||||
.property_is_writeable = usb_psy_prop_is_writeable,
|
||||
};
|
||||
|
||||
static int xm_init_usb_psy(struct xm_charger *charger)
|
||||
{
|
||||
struct power_supply_config usb_cfg = {};
|
||||
int rc = 0;
|
||||
|
||||
usb_cfg.drv_data = charger;
|
||||
usb_cfg.of_node = charger->dev->of_node;
|
||||
charger->usb_psy = devm_power_supply_register(charger->dev,
|
||||
&usb_psy_desc, &usb_cfg);
|
||||
if (IS_ERR(charger->usb_psy)) {
|
||||
charger_err("%s: Couldn't register USB power supply\n",
|
||||
__func__);
|
||||
return PTR_ERR(charger->usb_psy);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xm_charger_parse_dt(struct xm_charger *charger)
|
||||
{
|
||||
struct device_node *node = charger->dev->of_node;
|
||||
int rc = 0;
|
||||
|
||||
if (!node) {
|
||||
charger_err("%s: device tree node missing\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "xm,fv-max-uv",
|
||||
&charger->dt.chg_voltage_max);
|
||||
if (rc < 0)
|
||||
charger->dt.chg_voltage_max = 4450000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,fcc-max-ua",
|
||||
&charger->dt.batt_current_max);
|
||||
if (rc < 0)
|
||||
charger->dt.batt_current_max = 10000000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,fv-max-design-uv",
|
||||
&charger->dt.chg_design_voltage_max);
|
||||
if (rc < 0)
|
||||
charger->dt.chg_design_voltage_max = 10000000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,icl-max-ua",
|
||||
&charger->dt.input_batt_current_max);
|
||||
if (rc < 0)
|
||||
charger->dt.input_batt_current_max = 3000000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,step-chg-enable",
|
||||
&charger->dt.step_chg_enable);
|
||||
if (rc < 0)
|
||||
charger->dt.step_chg_enable = false;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,sw-jeita-enable",
|
||||
&charger->dt.sw_jeita_enable);
|
||||
if (rc < 0)
|
||||
charger->dt.sw_jeita_enable = false;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,ffc_ieoc_l",
|
||||
&charger->dt.ffc_ieoc_l);
|
||||
if (rc < 0)
|
||||
charger->dt.ffc_ieoc_l = 850000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,ffc_ieoc_h",
|
||||
&charger->dt.ffc_ieoc_h);
|
||||
if (rc < 0)
|
||||
charger->dt.ffc_ieoc_h = 890000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,non_ffc_ieoc",
|
||||
&charger->dt.non_ffc_ieoc);
|
||||
if (rc < 0)
|
||||
charger->dt.non_ffc_ieoc = 200000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,non_ffc_cv",
|
||||
&charger->dt.non_ffc_cv);
|
||||
if (rc < 0)
|
||||
charger->dt.non_ffc_cv = 4480000;
|
||||
|
||||
rc = of_property_read_u32(node, "xm,non_ffc_cc",
|
||||
&charger->dt.non_ffc_cc);
|
||||
if (rc < 0)
|
||||
charger->dt.non_ffc_cc = 4000000;
|
||||
|
||||
rc = xm_charger_parse_dt_therm(charger);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't initialize parse_dt_therm rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
rc = xm_get_iio_channel(g_battmngr_iio, "chg_pump_therm",
|
||||
&g_battmngr_iio->chg_pump_therm);
|
||||
if (rc < 0)
|
||||
charger_err("%s: Couldn't get IIO channel chg_pump_therm rc = %d\n",
|
||||
__func__, rc);
|
||||
*/
|
||||
rc = xm_get_iio_channel(g_battmngr_iio, "typec_conn_therm",
|
||||
&g_battmngr_iio->typec_conn_therm);
|
||||
if (rc < 0)
|
||||
charger_err(
|
||||
"%s: Couldn't get IIO channel typec_conn_therm rc = %d\n",
|
||||
__func__, rc);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int xm_charger_init(struct xm_charger *charger)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
charger_err("%s: Start\n", __func__);
|
||||
|
||||
rc = xm_charger_parse_dt(charger);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't parse device tree rc=%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
device_init_wakeup(charger->dev, true);
|
||||
|
||||
rc = xm_charger_create_votable(charger);
|
||||
if (rc < 0) {
|
||||
charger_err(
|
||||
"%s: Couldn't init xm_charger_create_votable rc=%d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_init_usb_psy(charger);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't initialize usb psy rc=%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_stepchg_jeita_init(charger, charger->dt.step_chg_enable,
|
||||
charger->dt.sw_jeita_enable);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't init xm_stepchg_jeita_init rc=%d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_chg_feature_init(charger);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't init xm_chg_feature_init rc=%d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
charger->batt_psy = power_supply_get_by_name("battery");
|
||||
if (!charger->usb_psy)
|
||||
charger_err("%s get batt psy fail\n", __func__);
|
||||
|
||||
chg_votable_init(charger);
|
||||
xm_charger_thermal(charger);
|
||||
|
||||
charger_err("%s: End\n", __func__);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_charger_init);
|
||||
|
||||
void xm_charger_deinit(void)
|
||||
{
|
||||
xm_step_chg_deinit();
|
||||
xm_chg_feature_deinit();
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_charger_deinit);
|
||||
|
||||
MODULE_DESCRIPTION("Xiaomi charger core");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -0,0 +1,438 @@
|
||||
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
#include <linux/battmngr/xm_battery_core.h>
|
||||
|
||||
typedef void (*usb_host_cb)(void);
|
||||
usb_host_cb stop_usb_host_cb = NULL;
|
||||
usb_host_cb start_usb_host_cb = NULL;
|
||||
|
||||
void stop_usb_host_cb_set(usb_host_cb cb)
|
||||
{
|
||||
stop_usb_host_cb = cb;
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(stop_usb_host_cb_set);
|
||||
|
||||
void start_usb_host_cb_set(usb_host_cb cb)
|
||||
{
|
||||
start_usb_host_cb = cb;
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(start_usb_host_cb_set);
|
||||
|
||||
static int xm_set_vbus_disable(struct chg_feature_info *chip, bool disable)
|
||||
{
|
||||
int ret = 0;
|
||||
static bool conn_therm_flag = false;
|
||||
|
||||
charger_err("%s: set vbus disable:%d\n", __func__, disable);
|
||||
|
||||
if (disable && !conn_therm_flag) {
|
||||
conn_therm_flag = true;
|
||||
gpio_set_value(chip->vbus_ctrl_gpio, 1);
|
||||
if (stop_usb_host_cb)
|
||||
stop_usb_host_cb();
|
||||
} else if (!disable && conn_therm_flag) {
|
||||
conn_therm_flag = false;
|
||||
gpio_set_value(chip->vbus_ctrl_gpio, 0);
|
||||
if (start_usb_host_cb)
|
||||
start_usb_host_cb();
|
||||
}
|
||||
|
||||
chip->vbus_disable = disable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int typec_conn_therm_start_stop(struct xm_charger *charger,
|
||||
struct chg_feature_info *info)
|
||||
{
|
||||
charger_err("%s\n", __func__);
|
||||
|
||||
if (g_xm_charger->bc12_active || g_xm_charger->pd_active ||
|
||||
g_xm_charger->otg_enable) {
|
||||
cancel_delayed_work_sync(&info->typec_conn_therm_work);
|
||||
schedule_delayed_work(&info->typec_conn_therm_work,
|
||||
msecs_to_jiffies(CONN_THERM_DELAY_5S));
|
||||
} else {
|
||||
if (!info->vbus_disable)
|
||||
cancel_delayed_work_sync(&info->typec_conn_therm_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void typec_conn_therm_work(struct work_struct *work)
|
||||
{
|
||||
struct chg_feature_info *chip = container_of(
|
||||
work, struct chg_feature_info, typec_conn_therm_work.work);
|
||||
//union power_supply_propval pval = {0, };
|
||||
int ret = 0;
|
||||
|
||||
if (!g_battmngr_iio->typec_conn_therm) {
|
||||
xm_get_iio_channel(g_battmngr_iio, "typec_conn_therm",
|
||||
&g_battmngr_iio->typec_conn_therm);
|
||||
}
|
||||
|
||||
if (g_battmngr_iio->typec_conn_therm) {
|
||||
ret = iio_read_channel_processed(
|
||||
g_battmngr_iio->typec_conn_therm,
|
||||
&chip->connector_temp);
|
||||
if (ret < 0) {
|
||||
charger_err("Couldn't read connector_temp, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
chip->connector_temp /= 100;
|
||||
} else {
|
||||
charger_err("Failed to get IIO channel typec_conn_therm\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (chip->fake_conn_temp != 0)
|
||||
chip->connector_temp = chip->fake_conn_temp;
|
||||
|
||||
if (chip->connector_temp >= CONN_THERM_TOOHIG_70DEC)
|
||||
xm_set_vbus_disable(chip, true);
|
||||
else if (chip->connector_temp <
|
||||
(CONN_THERM_TOOHIG_70DEC - CONN_THERM_HYS_2DEC))
|
||||
xm_set_vbus_disable(chip, false);
|
||||
/*
|
||||
ret = power_supply_get_property(g_xm_charger->usb_psy,
|
||||
POWER_SUPPLY_PROP_ONLINE, &pval);
|
||||
if (ret < 0) {
|
||||
charger_err("%s: Get usb online failed, rc=%d\n",
|
||||
__func__, ret);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
schedule_delayed_work(&chip->typec_conn_therm_work,
|
||||
msecs_to_jiffies(CONN_THERM_DELAY_5S));
|
||||
charger_err("%s: fake_conn_temp = %d, connector_temp = %d\n", __func__,
|
||||
chip->fake_conn_temp, chip->connector_temp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int xm_get_adapter_power_max(void)
|
||||
{
|
||||
int val;
|
||||
int apdo_max = 0;
|
||||
int apdo_max_volt, apdo_max_curr;
|
||||
|
||||
if (g_xm_charger->input_suspend == 1)
|
||||
return 0;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_APDO_VOLT_MAX,
|
||||
&apdo_max_volt);
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_APDO_CURR_MAX,
|
||||
&apdo_max_curr);
|
||||
apdo_max = (apdo_max_volt * apdo_max_curr) / 1000000;
|
||||
charger_err("apdo_max:%d\n", apdo_max);
|
||||
|
||||
if (apdo_max == 65)
|
||||
val = APDO_MAX_65W; /* only for J1 65W adapter */
|
||||
else if (apdo_max >= 60)
|
||||
val = APDO_MAX_67W;
|
||||
else if (apdo_max >= 55 && apdo_max < 60)
|
||||
val = APDO_MAX_55W;
|
||||
else if (apdo_max >= 50 && apdo_max < 55)
|
||||
val = APDO_MAX_50W;
|
||||
else
|
||||
val = apdo_max;
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_get_adapter_power_max);
|
||||
|
||||
int xm_get_soc_decimal(void)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_SOC_DECIMAL,
|
||||
&val);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_get_soc_decimal);
|
||||
|
||||
int xm_get_soc_decimal_rate(void)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_SOC_DECIMAL_RATE, &val);
|
||||
if (val > 100 || val < 0)
|
||||
val = 0;
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_get_soc_decimal_rate);
|
||||
|
||||
struct quick_charge adapter_cap[11] = {
|
||||
{ POWER_SUPPLY_USB_TYPE_SDP, QUICK_CHARGE_NORMAL },
|
||||
{ POWER_SUPPLY_USB_TYPE_DCP, QUICK_CHARGE_NORMAL },
|
||||
{ POWER_SUPPLY_USB_TYPE_CDP, QUICK_CHARGE_NORMAL },
|
||||
{ POWER_SUPPLY_USB_TYPE_ACA, QUICK_CHARGE_NORMAL },
|
||||
{ POWER_SUPPLY_TYPE_USB_FLOAT, QUICK_CHARGE_NORMAL },
|
||||
{ POWER_SUPPLY_USB_TYPE_PD, QUICK_CHARGE_FAST },
|
||||
{ POWER_SUPPLY_TYPE_USB_HVDCP, QUICK_CHARGE_FAST },
|
||||
{ POWER_SUPPLY_TYPE_USB_HVDCP_3, QUICK_CHARGE_FAST },
|
||||
{ POWER_SUPPLY_TYPE_USB_HVDCP_3P5, QUICK_CHARGE_FAST },
|
||||
{ POWER_SUPPLY_TYPE_WIRELESS, QUICK_CHARGE_FAST },
|
||||
{ 0, 0 },
|
||||
};
|
||||
int xm_get_quick_charge_type(void)
|
||||
{
|
||||
int val, i = 0;
|
||||
int power_max = 0;
|
||||
int real_charger_type;
|
||||
|
||||
if (g_xm_charger->pd_active) {
|
||||
real_charger_type = POWER_SUPPLY_USB_TYPE_PD;
|
||||
} else {
|
||||
real_charger_type = g_xm_charger->bc12_type;
|
||||
}
|
||||
|
||||
charger_err("real charger type : %d\n ", real_charger_type);
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TEMP, &val);
|
||||
if ((val >= BATT_WARM_THRESHOLD) || (val < LIGHTING_ICON_CHANGE)) {
|
||||
charger_err("battery temp is under 5 or above 48\n");
|
||||
return QUICK_CHARGE_NORMAL;
|
||||
} else if ((real_charger_type == POWER_SUPPLY_USB_TYPE_PD) &&
|
||||
g_xm_charger->pd_verified) {
|
||||
power_max = xm_get_adapter_power_max();
|
||||
charger_err("power_max : %d\n ", power_max);
|
||||
|
||||
if (power_max >= 50)
|
||||
return QUICK_CHARGE_SUPER;
|
||||
else
|
||||
return QUICK_CHARGE_TURBE;
|
||||
} else if (real_charger_type == POWER_SUPPLY_USB_TYPE_PD) {
|
||||
return QUICK_CHARGE_FAST;
|
||||
} else {
|
||||
while (adapter_cap[i].adap_type != 0) {
|
||||
if (real_charger_type == adapter_cap[i].adap_type) {
|
||||
return adapter_cap[i].adap_cap;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_get_quick_charge_type);
|
||||
|
||||
#define MAX_UEVENT_LENGTH 50
|
||||
static void generate_xm_charge_uvent(struct work_struct *work)
|
||||
{
|
||||
int count;
|
||||
struct chg_feature_info *chip = container_of(
|
||||
work, struct chg_feature_info, xm_prop_change_work.work);
|
||||
|
||||
static char uevent_string[][MAX_UEVENT_LENGTH + 1] = {
|
||||
"POWER_SUPPLY_SOC_DECIMAL=\n", //length=31+8
|
||||
"POWER_SUPPLY_SOC_DECIMAL_RATE=\n", //length=31+8
|
||||
"POWER_SUPPLY_QUICK_CHARGE_TYPE=\n",
|
||||
};
|
||||
static char *envp[] = {
|
||||
uevent_string[0],
|
||||
uevent_string[1],
|
||||
uevent_string[2],
|
||||
NULL,
|
||||
};
|
||||
char *prop_buf = NULL;
|
||||
|
||||
count = chip->update_cont;
|
||||
if (chip->update_cont < 0)
|
||||
return;
|
||||
|
||||
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!prop_buf)
|
||||
return;
|
||||
|
||||
scnprintf(prop_buf, PAGE_SIZE, "%u", xm_get_soc_decimal());
|
||||
strncpy(uevent_string[0] + 25, prop_buf, MAX_UEVENT_LENGTH - 25);
|
||||
|
||||
scnprintf(prop_buf, PAGE_SIZE, "%u", xm_get_soc_decimal_rate());
|
||||
strncpy(uevent_string[1] + 30, prop_buf, MAX_UEVENT_LENGTH - 30);
|
||||
|
||||
scnprintf(prop_buf, PAGE_SIZE, "%u", xm_get_quick_charge_type());
|
||||
strncpy(uevent_string[2] + 31, prop_buf, MAX_UEVENT_LENGTH - 31);
|
||||
|
||||
charger_err("uevent test : %s, %s, %s, count %d\n", envp[0], envp[1],
|
||||
envp[2], count);
|
||||
|
||||
/*add our prop end*/
|
||||
|
||||
kobject_uevent_env(&chip->dev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
free_page((unsigned long)prop_buf);
|
||||
chip->update_cont = count - 1;
|
||||
schedule_delayed_work(&chip->xm_prop_change_work,
|
||||
msecs_to_jiffies(CONN_THERM_DELAY_2S));
|
||||
return;
|
||||
}
|
||||
|
||||
static int xm_get_prop_battery_input_suspend(void)
|
||||
{
|
||||
int val;
|
||||
val = (get_client_vote(g_xm_charger->fcc_votable, USER_VOTER) == 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static int xm_set_prop_battery_input_suspend(int val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* vote 0mA when battery suspended */
|
||||
rc = vote(g_xm_charger->fcc_votable, USER_VOTER, (bool)val, 0);
|
||||
if (rc < 0) {
|
||||
battery_err("%s: Couldn't vote to %s fcc rc=%d\n", __func__,
|
||||
(bool)val ? "suspend" : "resume", rc);
|
||||
return rc;
|
||||
}
|
||||
g_xm_charger->chg_feature->battery_input_suspend = val;
|
||||
|
||||
power_supply_changed(g_xm_charger->batt_psy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void night_chargig_change_work(struct work_struct *work)
|
||||
{
|
||||
struct chg_feature_info *chip = container_of(
|
||||
work, struct chg_feature_info, night_chargig_change_work.work);
|
||||
|
||||
static int pre_night_chg_flag = 0;
|
||||
int capacity, battery_input_suspend;
|
||||
int rc = 0, val = 0;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CAPACITY, &capacity);
|
||||
battery_input_suspend = xm_get_prop_battery_input_suspend();
|
||||
battery_err("%s: capacity:%d, battery_input_suspend:%d.\n", __func__,
|
||||
capacity, battery_input_suspend);
|
||||
battery_err("%s: pre_nchg:%d, nchg:%d.\n", __func__, pre_night_chg_flag,
|
||||
g_xm_charger->chg_feature->night_chg_flag);
|
||||
|
||||
if (pre_night_chg_flag != g_xm_charger->chg_feature->night_chg_flag) {
|
||||
if (g_xm_charger->chg_feature->night_chg_flag &&
|
||||
capacity >= 80) {
|
||||
val = 1;
|
||||
rc = xm_set_prop_battery_input_suspend(val);
|
||||
battery_err("%s: open night charging.\n", __func__);
|
||||
pre_night_chg_flag =
|
||||
g_xm_charger->chg_feature->night_chg_flag;
|
||||
}
|
||||
}
|
||||
|
||||
if (battery_input_suspend &&
|
||||
(!g_xm_charger->chg_feature->night_chg_flag || capacity <= 75)) {
|
||||
val = 0;
|
||||
xm_set_prop_battery_input_suspend(val);
|
||||
battery_err("%s: close night charging.\n", __func__);
|
||||
pre_night_chg_flag = 0;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&chip->night_chargig_change_work,
|
||||
msecs_to_jiffies(CONN_THERM_DELAY_10S));
|
||||
return;
|
||||
}
|
||||
|
||||
int night_charging_start_stop(struct xm_charger *charger,
|
||||
struct chg_feature_info *chip)
|
||||
{
|
||||
charger_err("%s\n", __func__);
|
||||
|
||||
if (g_xm_charger->bc12_active || g_xm_charger->pd_active) {
|
||||
cancel_delayed_work_sync(&chip->night_chargig_change_work);
|
||||
schedule_delayed_work(&chip->night_chargig_change_work, 0);
|
||||
} else {
|
||||
cancel_delayed_work_sync(&chip->night_chargig_change_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_chg_feature_parse_dt(struct device *dev,
|
||||
struct chg_feature_info *chip)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
chip->vbus_ctrl_gpio = of_get_named_gpio(np, "vbus_ctrl_gpio", 0);
|
||||
if (chip->vbus_ctrl_gpio < 0) {
|
||||
charger_err("%s no vbus_ctrl_gpio info\n", __func__);
|
||||
return ret;
|
||||
} else {
|
||||
charger_err("%s vbus_ctrl_gpio info %d\n", __func__,
|
||||
chip->vbus_ctrl_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xm_chg_feature_init(struct xm_charger *charger)
|
||||
{
|
||||
int ret;
|
||||
struct chg_feature_info *chip;
|
||||
|
||||
charger_err("%s: Start\n", __func__);
|
||||
|
||||
if (charger->chg_feature) {
|
||||
charger_err("%s: Already initialized\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(charger->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
chip->dev = charger->dev;
|
||||
chip->update_cont = 0;
|
||||
|
||||
ret = xm_chg_feature_parse_dt(chip->dev, chip);
|
||||
if (ret) {
|
||||
charger_err("%s parse dt fail(%d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(chip->dev, chip->vbus_ctrl_gpio,
|
||||
"vbus ctrl gpio");
|
||||
if (ret) {
|
||||
charger_err("%s: %d gpio request failed\n", __func__,
|
||||
chip->vbus_ctrl_gpio);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_output(chip->vbus_ctrl_gpio, false);
|
||||
|
||||
INIT_DELAYED_WORK(&chip->typec_conn_therm_work, typec_conn_therm_work);
|
||||
INIT_DELAYED_WORK(&chip->xm_prop_change_work, generate_xm_charge_uvent);
|
||||
INIT_DELAYED_WORK(&chip->night_chargig_change_work,
|
||||
night_chargig_change_work);
|
||||
|
||||
schedule_delayed_work(&chip->typec_conn_therm_work,
|
||||
msecs_to_jiffies(CONN_THERM_DELAY_5S));
|
||||
schedule_delayed_work(&chip->xm_prop_change_work,
|
||||
msecs_to_jiffies(30000));
|
||||
|
||||
charger->chg_feature = chip;
|
||||
charger_err("%s: End\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xm_chg_feature_deinit(void)
|
||||
{
|
||||
struct chg_feature_info *chip = g_xm_charger->chg_feature;
|
||||
|
||||
if (!chip)
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&chip->typec_conn_therm_work);
|
||||
cancel_delayed_work_sync(&chip->xm_prop_change_work);
|
||||
cancel_delayed_work_sync(&chip->night_chargig_change_work);
|
||||
chip = NULL;
|
||||
|
||||
return;
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
|
||||
static int xm_charger_config_iterm(struct xm_charger *charger, int mode)
|
||||
{
|
||||
int rc = 0, val = 0;
|
||||
int batt_temp;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TEMP,
|
||||
&val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get battery temp:%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
batt_temp = val;
|
||||
|
||||
if (mode) {
|
||||
if (batt_temp >= BATT_NORMAL_H_THRESHOLD)
|
||||
val = charger->dt.ffc_ieoc_h;
|
||||
else
|
||||
val = charger->dt.ffc_ieoc_l;
|
||||
} else {
|
||||
val = charger->dt.non_ffc_ieoc;
|
||||
}
|
||||
val /= 1000;
|
||||
|
||||
charger_err("%s: charger_config_iterm:%d\n", __func__, val);
|
||||
rc = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_TERM, val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get charge term:%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xm_charger_get_fastcharge_mode(struct xm_charger *charger, int *mode)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_FASTCHARGE_MODE, mode);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't write fastcharge mode:%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xm_charger_set_fastcharge_mode(struct xm_charger *charger, int mode)
|
||||
{
|
||||
int rc = 0, val = 0;
|
||||
int batt_temp;
|
||||
int effective_fv = 0;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_BATTERY_AUTH, &val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get battery authentic:%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
charger_err("%s: get battery authentic:%d\n", __func__, val);
|
||||
if (!val)
|
||||
mode = false;
|
||||
|
||||
/*if soc > 95 do not set fastcharge flag*/
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_CAPACITY, &val);
|
||||
charger_err("%s: get fg capacity:%d\n", __func__, val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get fg capacity:%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
if (mode && val >= SOC_HIGH_THRESHOLD) {
|
||||
charger_err(
|
||||
"%s: soc:%d is more than 95, do not setfastcharge mode\n",
|
||||
__func__, val);
|
||||
mode = false;
|
||||
}
|
||||
|
||||
/*if temp > 480 or temp < 150 do not set fastcharge flag*/
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TEMP,
|
||||
&val);
|
||||
charger_err("%s: get fg temp:%d\n", __func__, val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get fg temp:%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
batt_temp = val;
|
||||
if (mode &&
|
||||
(val >= BATT_WARM_THRESHOLD || val <= BATT_COOL_THRESHOLD)) {
|
||||
charger_err("%s: temp:%d is abort, do not setfastcharge mode\n",
|
||||
__func__, val);
|
||||
mode = false;
|
||||
}
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_CHIP_OK,
|
||||
&val);
|
||||
charger_err("%s: get fg chip_ok:%d\n", __func__, val);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't get fg chip_ok:%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
if (mode && !val) {
|
||||
charger_err("%s: chip_ok is :%d, do not setfastcharge mode\n",
|
||||
__func__, val);
|
||||
mode = false;
|
||||
}
|
||||
|
||||
rc = xm_battmngr_write_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_FASTCHARGE_MODE, mode);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't write fastcharge mode:%d\n", __func__,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
xm_charger_config_iterm(charger, mode);
|
||||
xm_charger_thermal(charger);
|
||||
|
||||
vote(charger->fcc_votable, FFC_MODE_VOTER, !mode,
|
||||
charger->dt.non_ffc_cc);
|
||||
vote(charger->fv_votable, FFC_MODE_VOTER, !mode,
|
||||
charger->dt.non_ffc_cv);
|
||||
|
||||
if (charger->smartBatVal) {
|
||||
effective_fv = get_effective_result(charger->fv_votable);
|
||||
charger_err("%s: Now fastcharge mode effective FV: %d\n",
|
||||
__func__, effective_fv);
|
||||
|
||||
if (mode) {
|
||||
vote(charger->fv_votable, SMART_BATTERY_FV, false, 0);
|
||||
vote(charger->smart_batt_votable, SMART_BATTERY_FV,
|
||||
true, charger->smartBatVal);
|
||||
} else {
|
||||
vote(charger->smart_batt_votable, SMART_BATTERY_FV,
|
||||
true, 0);
|
||||
vote(charger->fv_votable, SMART_BATTERY_FV, true,
|
||||
charger->dt.non_ffc_cv -
|
||||
charger->smartBatVal * 1000);
|
||||
}
|
||||
} else {
|
||||
if (mode)
|
||||
vote(charger->smart_batt_votable, SMART_BATTERY_FV,
|
||||
true, 0);
|
||||
else
|
||||
vote(charger->fv_votable, SMART_BATTERY_FV, false, 0);
|
||||
charger_err("%s: Cancel fastcharge mode effective FV: %d\n",
|
||||
__func__,
|
||||
get_effective_result(charger->fv_votable));
|
||||
}
|
||||
|
||||
charger_err("%s: fastcharge mode:%d\n", __func__, mode);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
|
||||
int xm_charger_parse_dt_therm(struct xm_charger *charger)
|
||||
{
|
||||
struct device_node *node = charger->dev->of_node;
|
||||
int rc = 0, byte_len;
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation-icl", &byte_len)) {
|
||||
charger->dt.thermal_mitigation_icl =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation_icl == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(
|
||||
node, "xm,thermal-mitigation-icl",
|
||||
charger->dt.thermal_mitigation_icl,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation", &byte_len)) {
|
||||
charger->dt.thermal_mitigation =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(node, "xm,thermal-mitigation",
|
||||
charger->dt.thermal_mitigation,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation-dcp", &byte_len)) {
|
||||
charger->dt.thermal_mitigation_dcp =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation_dcp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(
|
||||
node, "xm,thermal-mitigation-dcp",
|
||||
charger->dt.thermal_mitigation_dcp,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation-qc2", &byte_len)) {
|
||||
charger->dt.thermal_mitigation_qc2 =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation_qc2 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(
|
||||
node, "xm,thermal-mitigation-qc2",
|
||||
charger->dt.thermal_mitigation_qc2,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation-pd", &byte_len)) {
|
||||
charger->dt.thermal_mitigation_pd =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation_pd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(
|
||||
node, "xm,thermal-mitigation-pd",
|
||||
charger->dt.thermal_mitigation_pd,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(node, "xm,thermal-mitigation-pd-cp", &byte_len)) {
|
||||
charger->dt.thermal_mitigation_pd_cp =
|
||||
devm_kzalloc(charger->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (charger->dt.thermal_mitigation_pd_cp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->thermal_levels = byte_len / sizeof(u32);
|
||||
rc = of_property_read_u32_array(
|
||||
node, "xm,thermal-mitigation-pd-cp",
|
||||
charger->dt.thermal_mitigation_pd_cp,
|
||||
charger->thermal_levels);
|
||||
if (rc < 0) {
|
||||
charger_err("%s: Couldn't read threm limits rc = %d\n",
|
||||
__func__, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int xm_charger_thermal(struct xm_charger *charger)
|
||||
{
|
||||
int thermal_fcc_ua = 0;
|
||||
int rc = 0;
|
||||
|
||||
if ((charger->system_temp_level >= MAX_TEMP_LEVEL) ||
|
||||
(charger->system_temp_level < 0))
|
||||
return -EINVAL;
|
||||
|
||||
switch (charger->real_type) {
|
||||
case POWER_SUPPLY_TYPE_USB_PD:
|
||||
if (charger->pd_active == QTI_POWER_SUPPLY_PD_PPS_ACTIVE) {
|
||||
thermal_fcc_ua = charger->dt.thermal_mitigation_pd_cp
|
||||
[charger->system_temp_level];
|
||||
} else {
|
||||
thermal_fcc_ua = charger->dt.thermal_mitigation_pd
|
||||
[charger->system_temp_level];
|
||||
g_battmngr_noti->misc_msg.thermal_icl =
|
||||
charger->dt.thermal_mitigation_icl
|
||||
[charger->system_temp_level];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (charger->bc12_type == POWER_SUPPLY_TYPE_USB_HVDCP) {
|
||||
g_battmngr_noti->misc_msg.thermal_icl =
|
||||
charger->dt.thermal_mitigation_qc2
|
||||
[charger->system_temp_level];
|
||||
} else {
|
||||
thermal_fcc_ua = charger->dt.thermal_mitigation
|
||||
[charger->system_temp_level];
|
||||
g_battmngr_noti->misc_msg.thermal_icl =
|
||||
charger->dt.thermal_mitigation_dcp
|
||||
[charger->system_temp_level];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_battmngr_noti->misc_msg.disable_thermal && charger->pd_active) {
|
||||
if (charger->pd_active == QTI_POWER_SUPPLY_PD_PPS_ACTIVE) {
|
||||
thermal_fcc_ua =
|
||||
charger->dt.thermal_mitigation_pd_cp[0];
|
||||
} else {
|
||||
thermal_fcc_ua = charger->dt.thermal_mitigation_pd[0];
|
||||
}
|
||||
}
|
||||
|
||||
charger_err(
|
||||
"%s: chg->system_temp_level: %d, thermal_icl:%d, real_type:%d, thermal_fcc_ua is %d, pd_active = %d, bc12_type = %d\n",
|
||||
__func__, charger->system_temp_level,
|
||||
g_battmngr_noti->misc_msg.thermal_icl, charger->real_type,
|
||||
thermal_fcc_ua, charger->pd_active, charger->bc12_type);
|
||||
|
||||
if (charger->system_temp_level == 0) {
|
||||
rc = vote(charger->fcc_votable, THERMAL_DAEMON_VOTER, false, 0);
|
||||
if (rc < 0)
|
||||
charger_err(
|
||||
"%s: Couldn't disable USB thermal FCC vote rc = %d\n",
|
||||
__func__, rc);
|
||||
} else {
|
||||
if (thermal_fcc_ua > 0) {
|
||||
rc = vote(charger->fcc_votable, THERMAL_DAEMON_VOTER,
|
||||
true, thermal_fcc_ua);
|
||||
if (rc < 0)
|
||||
charger_err(
|
||||
"%s: Couldn't enable USB thermal FCC vote rc = %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
}
|
||||
rerun_election(charger->fcc_votable);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(xm_charger_thermal);
|
@ -0,0 +1,167 @@
|
||||
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
|
||||
static int xm_charger_fcc_vote_callback(struct votable *votable, void *data,
|
||||
int value, const char *client)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
charger_err("%s: vote FCC = %d, sw_disable = %d\n", __func__, value,
|
||||
g_battmngr_noti->mainchg_msg.sw_disable);
|
||||
|
||||
if (g_battmngr_noti->mainchg_msg.sw_disable)
|
||||
value = MAIN_MIN_FCC;
|
||||
|
||||
ret = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_CURRENT, value / 1000);
|
||||
if (ret) {
|
||||
charger_err("%s: failed to set FCC\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xm_charger_fv_vote_callback(struct votable *votable, void *data,
|
||||
int value, const char *client)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
charger_err("%s: vote FV = %d\n", __func__, value);
|
||||
ret = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_VOLTAGE_TERM,
|
||||
value / 1000);
|
||||
if (ret) {
|
||||
charger_err("%s: failed to set FV\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xm_charger_icl_vote_callback(struct votable *votable, void *data,
|
||||
int value, const char *client)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
charger_err("%s: vote ICL = %d\n", __func__, value);
|
||||
ret = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_INPUT_CURRENT_SETTLED,
|
||||
value / 1000);
|
||||
if (ret) {
|
||||
charger_err("%s: failed to set ICL\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xm_charger_awake_vote_callback(struct votable *votable, void *data,
|
||||
int awake, const char *client)
|
||||
{
|
||||
struct xm_charger *charger = data;
|
||||
|
||||
if (awake)
|
||||
pm_stay_awake(charger->dev);
|
||||
else
|
||||
pm_relax(charger->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_charger_input_suspend_vote_callback(struct votable *votable,
|
||||
void *data, int value,
|
||||
const char *client)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
charger_err("%s: vote INPUT_SUSPEND = %d\n", __func__, value);
|
||||
ret = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_ENABLED, !value);
|
||||
if (ret) {
|
||||
charger_err("%s: failed to set INPUT_SUSPEND\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xm_charger_smart_batt_vote_callback(struct votable *votable,
|
||||
void *data, int value,
|
||||
const char *client)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
charger_err("%s: vote SMART_BATT = %d\n", __func__, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xm_charger_create_votable(struct xm_charger *charger)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
charger_err("%s: Start\n", __func__);
|
||||
|
||||
charger->fcc_votable = create_votable(
|
||||
"FCC", VOTE_MIN, xm_charger_fcc_vote_callback, charger);
|
||||
if (IS_ERR(charger->fcc_votable)) {
|
||||
rc = PTR_ERR(charger->fcc_votable);
|
||||
charger->fcc_votable = NULL;
|
||||
destroy_votable(charger->fcc_votable);
|
||||
charger_err("%s: failed to create voter FCC\n", __func__);
|
||||
}
|
||||
|
||||
charger->fv_votable = create_votable(
|
||||
"FV", VOTE_MIN, xm_charger_fv_vote_callback, charger);
|
||||
if (IS_ERR(charger->fv_votable)) {
|
||||
rc = PTR_ERR(charger->fv_votable);
|
||||
charger->fv_votable = NULL;
|
||||
destroy_votable(charger->fv_votable);
|
||||
charger_err("%s: failed to create voter FV\n", __func__);
|
||||
}
|
||||
|
||||
charger->usb_icl_votable = create_votable(
|
||||
"ICL", VOTE_MIN, xm_charger_icl_vote_callback, charger);
|
||||
if (IS_ERR(charger->usb_icl_votable)) {
|
||||
rc = PTR_ERR(charger->usb_icl_votable);
|
||||
charger->usb_icl_votable = NULL;
|
||||
destroy_votable(charger->usb_icl_votable);
|
||||
charger_err("%s: failed to create voter ICL\n", __func__);
|
||||
}
|
||||
|
||||
charger->awake_votable = create_votable(
|
||||
"AWAKE", VOTE_SET_ANY, xm_charger_awake_vote_callback, charger);
|
||||
if (IS_ERR(charger->awake_votable)) {
|
||||
rc = PTR_ERR(charger->awake_votable);
|
||||
charger->awake_votable = NULL;
|
||||
destroy_votable(charger->awake_votable);
|
||||
charger_err("%s: failed to create voter AWAKE\n", __func__);
|
||||
}
|
||||
|
||||
charger->input_suspend_votable =
|
||||
create_votable("INPUT_SUSPEND", VOTE_SET_ANY,
|
||||
xm_charger_input_suspend_vote_callback, charger);
|
||||
if (IS_ERR(charger->input_suspend_votable)) {
|
||||
rc = PTR_ERR(charger->input_suspend_votable);
|
||||
charger->input_suspend_votable = NULL;
|
||||
destroy_votable(charger->input_suspend_votable);
|
||||
charger_err("%s: failed to create voter INPUT_SUSPEND\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
charger->smart_batt_votable =
|
||||
create_votable("SMART_BATT", VOTE_MIN,
|
||||
xm_charger_smart_batt_vote_callback, charger);
|
||||
if (IS_ERR(charger->smart_batt_votable)) {
|
||||
rc = PTR_ERR(charger->smart_batt_votable);
|
||||
charger->smart_batt_votable = NULL;
|
||||
destroy_votable(charger->smart_batt_votable);
|
||||
charger_err("%s: failed to create voter SMART_BATT\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
charger_err("%s: End\n", __func__);
|
||||
|
||||
return rc;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xm_pd_mngr.o
|
@ -0,0 +1,349 @@
|
||||
|
||||
#ifndef XM_PD_MNGR_H_
|
||||
#define XM_PD_MNGR_H_
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/usb/usbpd.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include <linux/usb/tcpc/tcpm.h>
|
||||
#include <linux/battmngr/xm_battmngr_iio.h>
|
||||
#include <linux/battmngr/battmngr_voter.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
|
||||
enum pm_state {
|
||||
PD_PM_STATE_ENTRY,
|
||||
PD_PM_STATE_FC2_ENTRY,
|
||||
PD_PM_STATE_FC2_ENTRY_1,
|
||||
PD_PM_STATE_FC2_ENTRY_2,
|
||||
PD_PM_STATE_FC2_ENTRY_3,
|
||||
PD_PM_STATE_FC2_TUNE,
|
||||
PD_PM_STATE_FC2_EXIT,
|
||||
};
|
||||
|
||||
#define PROBE_CNT_MAX 50
|
||||
|
||||
#define PCA_PPS_CMD_RETRY_COUNT 2
|
||||
|
||||
#define PD_SRC_PDO_TYPE_FIXED 0
|
||||
#define PD_SRC_PDO_TYPE_BATTERY 1
|
||||
#define PD_SRC_PDO_TYPE_VARIABLE 2
|
||||
#define PD_SRC_PDO_TYPE_AUGMENTED 3
|
||||
|
||||
#define BATT_MAX_CHG_VOLT 4480
|
||||
#define BATT_FAST_CHG_CURR 6000
|
||||
#define BUS_OVP_THRESHOLD 12000
|
||||
#define BUS_OVP_ALARM_THRESHOLD 9500
|
||||
|
||||
#define BUS_VOLT_INIT_UP 800
|
||||
#define MIN_ADATPER_VOLTAGE_11V 11000
|
||||
#define CAPACITY_HIGH_THR 90
|
||||
|
||||
#define BAT_VOLT_LOOP_LMT BATT_MAX_CHG_VOLT
|
||||
#define BAT_CURR_LOOP_LMT BATT_FAST_CHG_CURR
|
||||
#define BUS_VOLT_LOOP_LMT BUS_OVP_THRESHOLD
|
||||
|
||||
#define PM_WORK_RUN_NORMAL_INTERVAL 500
|
||||
#define PM_WORK_RUN_QUICK_INTERVAL 200
|
||||
#define PM_WORK_RUN_CRITICAL_INTERVAL 100
|
||||
|
||||
enum {
|
||||
PM_ALGO_RET_OK,
|
||||
PM_ALGO_RET_THERM_FAULT,
|
||||
PM_ALGO_RET_OTHER_FAULT,
|
||||
PM_ALGO_RET_CHG_DISABLED,
|
||||
PM_ALGO_RET_TAPER_DONE,
|
||||
PM_ALGO_RET_UNSUPPORT_PPSTA,
|
||||
PM_ALGO_RET_NIGHT_CHARGING,
|
||||
};
|
||||
|
||||
enum adapter_cap_type {
|
||||
XM_PD_FIXED,
|
||||
XM_PD_APDO,
|
||||
XM_PD_APDO_START,
|
||||
XM_PD_APDO_END,
|
||||
};
|
||||
|
||||
#define BAT_OVP_FAULT_SHIFT 0
|
||||
#define BAT_OCP_FAULT_SHIFT 1
|
||||
#define BUS_OVP_FAULT_SHIFT 2
|
||||
#define BUS_OCP_FAULT_SHIFT 3
|
||||
#define BAT_THERM_FAULT_SHIFT 4
|
||||
#define BUS_THERM_FAULT_SHIFT 5
|
||||
#define DIE_THERM_FAULT_SHIFT 6
|
||||
|
||||
#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT)
|
||||
#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT)
|
||||
#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT)
|
||||
#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT)
|
||||
#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT)
|
||||
#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT)
|
||||
#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT)
|
||||
|
||||
#define BAT_OVP_ALARM_SHIFT 0
|
||||
#define BAT_OCP_ALARM_SHIFT 1
|
||||
#define BUS_OVP_ALARM_SHIFT 2
|
||||
#define BUS_OCP_ALARM_SHIFT 3
|
||||
#define BAT_THERM_ALARM_SHIFT 4
|
||||
#define BUS_THERM_ALARM_SHIFT 5
|
||||
#define DIE_THERM_ALARM_SHIFT 6
|
||||
#define BAT_UCP_ALARM_SHIFT 7
|
||||
|
||||
#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT)
|
||||
#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT)
|
||||
#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT)
|
||||
#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT)
|
||||
#define BAT_THERM_ALARM_MASK (1 << BAT_THERM_ALARM_SHIFT)
|
||||
#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT)
|
||||
#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT)
|
||||
#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT)
|
||||
|
||||
#define VBAT_REG_STATUS_SHIFT 0
|
||||
#define IBAT_REG_STATUS_SHIFT 1
|
||||
|
||||
#define VBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT)
|
||||
#define IBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT)
|
||||
|
||||
/* voters for usbpd */
|
||||
#define BQ_TAPER_FCC_VOTER "BQ_TAPER_FCC_VOTER"
|
||||
#define BQ_TAPER_CELL_HGIH_FCC_VOTER "BQ_TAPER_CELL_HGIH_FCC_VOTER"
|
||||
#define NON_PPS_PD_FCC_VOTER "NON_PPS_PD_FCC_VOTER"
|
||||
#define MAIN_CHG_ICL_VOTER "MAIN_CHG_ICL_VOTER"
|
||||
#define FFC_DISABLE_CP_FV_VOTER "FFC_DISABLE_CP_FV_VOTER"
|
||||
#define USER_VOTER "USER_VOTER"
|
||||
#define SMART_BATTERY_FV "SMART_BATTERY_FV"
|
||||
|
||||
#define MAIN_CHG_ICL (3000 * 1000)
|
||||
#define FFC_DISABLE_CP_FV (4560 * 1000)
|
||||
#define FFC_DISABLE_CP_FV_D (4576 * 1000)
|
||||
|
||||
/* defined min fcc threshold for start bq direct charging */
|
||||
#define START_DRIECT_CHARGE_FCC_MIN_THR 2000
|
||||
|
||||
/* product related */
|
||||
#define PPS_VOL_MAX 11000
|
||||
#define PPS_VOL_HYS 1000
|
||||
|
||||
#define STEP_MV 20
|
||||
#define TAPER_VOL_HYS 80
|
||||
#define TAPER_WITH_IBUS_HYS 60
|
||||
#define TAPER_IBUS_THR 450
|
||||
#define MAX_THERMAL_LEVEL 13
|
||||
#define MAX_THERMAL_LEVEL_FOR_DUAL_BQ 9
|
||||
#define THERMAL_LEVEL_11 11
|
||||
#define THERMAL_LEVEL_12 12
|
||||
#define THERMAL_11_VBUS_UP 300
|
||||
|
||||
#define FCC_MAX_MA_FOR_MASTER_BQ 6000
|
||||
#define IBUS_THRESHOLD_MA_FOR_DUAL_BQ 2100
|
||||
#define IBUS_THRESHOLD_MA_FOR_DUAL_BQ_LN8000 2500
|
||||
#define IBUS_THR_MA_HYS_FOR_DUAL_BQ 200
|
||||
#define IBUS_THR_TO_CLOSE_SLAVE_COUNT_MAX 40
|
||||
|
||||
/* jeita related */
|
||||
#define JEITA_WARM_THR 480
|
||||
#define JEITA_COOL_NOT_ALLOW_CP_THR 100
|
||||
/*
|
||||
* add hysteresis for warm threshold to avoid flash
|
||||
* charge and normal charge switch frequently at
|
||||
* the warm threshold
|
||||
*/
|
||||
#define JEITA_HYSTERESIS 20
|
||||
|
||||
#define BQ_TAPER_HYS_MV 10
|
||||
#define NON_FFC_BQ_TAPER_HYS_MV 50
|
||||
|
||||
#define BQ_TAPER_DECREASE_STEP_MA 200
|
||||
|
||||
#define CELL_VOLTAGE_HIGH_COUNT_MAX 2
|
||||
#define CELL_VOLTAGE_MAX_COUNT_MAX 1
|
||||
|
||||
#define HIGH_VOL_THR_MV 4380
|
||||
#define CRITICAL_HIGH_VOL_THR_MV 4480
|
||||
|
||||
#define TAPER_DONE_FFC_MA_LN8000 2500
|
||||
#define TAPER_DONE_NORMAL_MA 2200
|
||||
|
||||
#define VBAT_HIGH_FOR_FC_HYS_MV 100
|
||||
#define CAPACITY_TOO_HIGH_THR 95
|
||||
|
||||
#define CRITICAL_LOW_IBUS_THR 300
|
||||
|
||||
#define MAX_UNSUPPORT_PPS_CURRENT_MA 5500
|
||||
#define NON_PPS_PD_FCC_LIMIT (3000 * 1000)
|
||||
|
||||
#define POWER_SUPPLY_PD_ACTIVE QTI_POWER_SUPPLY_PD_ACTIVE
|
||||
#define POWER_SUPPLY_PD_PPS_ACTIVE QTI_POWER_SUPPLY_PD_PPS_ACTIVE
|
||||
|
||||
/* APDO */
|
||||
#define APDO_MAX_WATT 68000000
|
||||
#define APDO_MAX_MV 20000
|
||||
|
||||
enum {
|
||||
POWER_SUPPLY_PPS_INACTIVE = 0,
|
||||
POWER_SUPPLY_PPS_NON_VERIFIED,
|
||||
POWER_SUPPLY_PPS_VERIFIED,
|
||||
};
|
||||
|
||||
struct sw_device {
|
||||
bool charge_enabled;
|
||||
bool night_charging;
|
||||
};
|
||||
|
||||
struct cp_device {
|
||||
bool charge_enabled;
|
||||
bool batt_connecter_present;
|
||||
|
||||
bool batt_pres;
|
||||
bool vbus_pres;
|
||||
|
||||
/* alarm/fault status */
|
||||
bool bat_ovp_fault;
|
||||
bool bat_ocp_fault;
|
||||
bool bus_ovp_fault;
|
||||
bool bus_ocp_fault;
|
||||
|
||||
bool bat_ovp_alarm;
|
||||
bool bat_ocp_alarm;
|
||||
bool bus_ovp_alarm;
|
||||
bool bus_ocp_alarm;
|
||||
|
||||
bool bat_ucp_alarm;
|
||||
|
||||
bool bat_therm_alarm;
|
||||
bool bus_therm_alarm;
|
||||
bool die_therm_alarm;
|
||||
|
||||
bool bat_therm_fault;
|
||||
bool bus_therm_fault;
|
||||
bool die_therm_fault;
|
||||
|
||||
bool vbat_reg;
|
||||
bool ibat_reg;
|
||||
|
||||
int vbat_volt;
|
||||
int fg_vbat_mv;
|
||||
int vbus_volt;
|
||||
int ibat_curr;
|
||||
int ibus_curr;
|
||||
int bat_temp;
|
||||
int bus_temp;
|
||||
int die_temp;
|
||||
};
|
||||
|
||||
#define PM_STATE_LOG_MAX 32
|
||||
struct usbpd_pm {
|
||||
struct device *dev;
|
||||
struct tcpc_device *tcpc;
|
||||
|
||||
enum pm_state state;
|
||||
|
||||
struct cp_device cp;
|
||||
struct cp_device cp_sec;
|
||||
|
||||
struct sw_device sw;
|
||||
|
||||
int pd_active;
|
||||
bool pps_supported;
|
||||
bool fc2_exit_flag;
|
||||
|
||||
int request_voltage;
|
||||
int request_current;
|
||||
|
||||
int apdo_max_volt;
|
||||
int apdo_max_curr;
|
||||
int apdo_maxwatt;
|
||||
|
||||
struct delayed_work pm_work;
|
||||
struct delayed_work fc2_exit_work;
|
||||
|
||||
struct notifier_block nb;
|
||||
struct notifier_block battmngr_nb;
|
||||
struct notifier_block tcp_nb;
|
||||
|
||||
struct work_struct usb_psy_change_work;
|
||||
spinlock_t psy_change_lock;
|
||||
|
||||
struct votable *fcc_votable;
|
||||
struct votable *fv_votable;
|
||||
struct votable *usb_icl_votable;
|
||||
struct votable *input_suspend_votable;
|
||||
struct votable *smart_batt_votable;
|
||||
struct power_supply *batt_psy;
|
||||
struct power_supply *usb_psy;
|
||||
|
||||
/* dtsi properties */
|
||||
int bat_volt_max;
|
||||
int bat_curr_max;
|
||||
int bus_volt_max;
|
||||
int bus_curr_max;
|
||||
int non_ffc_bat_volt_max;
|
||||
int bus_curr_compensate;
|
||||
int therm_level_threshold;
|
||||
int pd_power_max;
|
||||
bool cp_sec_enable;
|
||||
/* jeita or thermal related */
|
||||
bool jeita_triggered;
|
||||
bool is_temp_out_fc2_range;
|
||||
int battery_warm_th;
|
||||
|
||||
/* bq taper related */
|
||||
int over_cell_vol_high_count;
|
||||
int over_cell_vol_max_count;
|
||||
int step_charge_high_vol_curr_max;
|
||||
int cell_vol_high_threshold_mv;
|
||||
int cell_vol_max_threshold_mv;
|
||||
|
||||
/* dual bq contrl related */
|
||||
bool no_need_en_slave_bq;
|
||||
int slave_bq_disabled_check_count;
|
||||
int master_ibus_below_critical_low_count;
|
||||
int chip_ok_count;
|
||||
int pd_auth_val;
|
||||
|
||||
/*unsupport pps ta check count*/
|
||||
int unsupport_pps_ta_check_count;
|
||||
|
||||
/*pca_pps_tcp_notifier_call*/
|
||||
bool is_pps_en_unlock;
|
||||
int hrst_cnt;
|
||||
};
|
||||
|
||||
struct pdpm_config {
|
||||
int bat_volt_lp_lmt; /*bat volt loop limit*/
|
||||
int bat_curr_lp_lmt;
|
||||
int bus_volt_lp_lmt;
|
||||
int bus_curr_lp_lmt;
|
||||
int bus_curr_compensate;
|
||||
|
||||
int fc2_taper_current;
|
||||
int fc2_steps;
|
||||
|
||||
int min_adapter_volt_required;
|
||||
int min_adapter_curr_required;
|
||||
int min_vbat_for_cp;
|
||||
|
||||
bool cp_sec_enable;
|
||||
bool fc2_disable_sw; /* disable switching charger during flash charge*/
|
||||
};
|
||||
|
||||
#endif /* XM_PD_MNGR_H_ */
|
1825
drivers/power/supply/xiaomi/battmngr/charger/quick_chg/xm_pd_mngr.c
Normal file
1825
drivers/power/supply/xiaomi/battmngr/charger/quick_chg/xm_pd_mngr.c
Normal file
File diff suppressed because it is too large
Load Diff
4
drivers/power/supply/xiaomi/battmngr/init/Makefile
Normal file
4
drivers/power/supply/xiaomi/battmngr/init/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += xm_battmngr.o
|
||||
xm_battmngr-objs := xm_battmngr_init.o xm_battmngr_class.o
|
@ -0,0 +1,42 @@
|
||||
|
||||
#ifndef __XM_BATTMNGR_INIT_H
|
||||
#define __XM_BATTMNGR_INIT_H
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/battmngr/battmngr_voter.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
#include <linux/battmngr/xm_battmngr_iio.h>
|
||||
#include <linux/battmngr/xm_charger_core.h>
|
||||
#include <linux/battmngr/xm_battery_core.h>
|
||||
|
||||
struct xm_battmngr {
|
||||
struct device *dev;
|
||||
struct class battmngr_class;
|
||||
|
||||
struct xm_battmngr_iio battmngr_iio;
|
||||
struct xm_battery battery;
|
||||
struct xm_charger charger;
|
||||
struct notifier_block battmngr_nb;
|
||||
struct battmngr_notify battmngr_noti;
|
||||
};
|
||||
|
||||
extern int get_verify_digest(char *buf);
|
||||
extern int set_verify_digest(u8 *rand_num);
|
||||
|
||||
int battmngr_class_init(struct xm_battmngr *battmngr);
|
||||
void battmngr_class_exit(struct xm_battmngr *battmngr);
|
||||
|
||||
#endif /* __XM_BATTMNGR_INIT_H */
|
897
drivers/power/supply/xiaomi/battmngr/init/xm_battmngr_class.c
Normal file
897
drivers/power/supply/xiaomi/battmngr/init/xm_battmngr_class.c
Normal file
@ -0,0 +1,897 @@
|
||||
|
||||
#include "inc/xm_battmngr_init.h"
|
||||
|
||||
/* Standard usb_type definitions similar to power_supply_sysfs.c */
|
||||
static const char *const power_supply_usb_type_text[] = {
|
||||
"Unknown", "USB", "USB_DCP", "USB_CDP", "USB_ACA",
|
||||
"USB_C", "USB_PD", "PD_DRP", "PD_PPS", "BrickID"
|
||||
};
|
||||
|
||||
/* Custom usb_type definitions */
|
||||
static const char *const qc_power_supply_usb_type_text[] = { "HVDCP", "HVDCP_3",
|
||||
"HVDCP_3P5",
|
||||
"USB_FLOAT" };
|
||||
|
||||
/* Custom typec mode definitions */
|
||||
static const char *const typec_mode_type_text[] = {
|
||||
"UNATTACHED", "ATTACHED_SNK", "ATTACHED_SRC",
|
||||
"ATTACHED_AUDIO", "ATTACHED_DEBUG", "ATTACHED_DBGACC_SNK",
|
||||
"ATTACHED_CUSTOM_SRC", "ATTACHED_NORP_SRC"
|
||||
};
|
||||
|
||||
static const char *get_typec_mode_name(int typec_mode)
|
||||
{
|
||||
int i;
|
||||
pr_err("%s: typec_mode=%d\n", __func__, typec_mode);
|
||||
|
||||
if (typec_mode < 0 || typec_mode > 7) {
|
||||
return "Unkown";
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(typec_mode_type_text); i++) {
|
||||
if (i == typec_mode)
|
||||
return typec_mode_type_text[i];
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char *get_usb_type_name(int usb_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_err("%s: usb_type=%d\n", __func__, usb_type);
|
||||
|
||||
if (usb_type >= POWER_SUPPLY_TYPE_USB_HVDCP &&
|
||||
usb_type <= POWER_SUPPLY_TYPE_USB_FLOAT) {
|
||||
for (i = 0; i < ARRAY_SIZE(qc_power_supply_usb_type_text);
|
||||
i++) {
|
||||
if (i == (usb_type - POWER_SUPPLY_TYPE_USB_HVDCP))
|
||||
return qc_power_supply_usb_type_text[i];
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(power_supply_usb_type_text); i++) {
|
||||
if (i == usb_type)
|
||||
return power_supply_usb_type_text[i];
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static ssize_t real_type_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int real_type = 0;
|
||||
|
||||
if (g_xm_charger->pd_active) {
|
||||
if (g_xm_charger->pd_active == QTI_POWER_SUPPLY_PD_ACTIVE)
|
||||
real_type = POWER_SUPPLY_USB_TYPE_PD;
|
||||
else if (g_xm_charger->pd_active ==
|
||||
QTI_POWER_SUPPLY_PD_PPS_ACTIVE)
|
||||
real_type = POWER_SUPPLY_USB_TYPE_PD_PPS;
|
||||
} else {
|
||||
real_type = g_xm_charger->bc12_type;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", get_usb_type_name(real_type));
|
||||
}
|
||||
static CLASS_ATTR_RO(real_type);
|
||||
|
||||
static ssize_t pd_verifed_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = g_xm_charger->pd_verified;
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t pd_verifed_store(struct class *c, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
g_xm_charger->pd_verified = val;
|
||||
g_battmngr_noti->pd_msg.msg_type = BATTMNGR_MSG_PD_VERIFED;
|
||||
g_battmngr_noti->pd_msg.pd_verified = g_xm_charger->pd_verified;
|
||||
battmngr_notifier_call_chain(BATTMNGR_EVENT_PD, g_battmngr_noti);
|
||||
|
||||
pr_err("pd_verified = %d\n", g_xm_charger->pd_verified);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(pd_verifed);
|
||||
|
||||
static ssize_t input_current_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val, temp;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &temp);
|
||||
val = temp;
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_SLAVE,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &temp);
|
||||
val += temp;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(input_current);
|
||||
|
||||
static ssize_t cp_ibus_slave_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_SLAVE,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_ibus_slave);
|
||||
|
||||
static ssize_t cp_ibus_master_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_ibus_master);
|
||||
|
||||
static ssize_t cp_ibus_delta_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val, val1, val2;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &val1);
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_SLAVE,
|
||||
CHARGE_PUMP_LN_BUS_CURRENT, &val2);
|
||||
val = val1 - val2;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_ibus_delta);
|
||||
|
||||
static ssize_t cp_present_slave_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_SLAVE,
|
||||
CHARGE_PUMP_LN_PRESENT, &val);
|
||||
pr_err("cp_present_slave_show = %d\n", val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_present_slave);
|
||||
|
||||
static ssize_t cp_present_master_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_PRESENT, &val);
|
||||
pr_err("cp_present_master_show = %d\n", val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_present_master);
|
||||
|
||||
static ssize_t cp_vbus_voltage_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_BUS_VOLTAGE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_vbus_voltage);
|
||||
|
||||
static ssize_t cp_vbat_voltage_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, CP_MASTER,
|
||||
CHARGE_PUMP_LN_BATTERY_VOLTAGE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cp_vbat_voltage);
|
||||
|
||||
static ssize_t vbus_voltage_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_BUS_VOLTAGE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(vbus_voltage);
|
||||
|
||||
static ssize_t vbat_voltage_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_VBAT_VOLTAGE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(vbat_voltage);
|
||||
|
||||
static ssize_t cell_voltage_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_VOLTAGE_NOW,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(cell_voltage);
|
||||
|
||||
static ssize_t soc_decimal_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = xm_get_soc_decimal();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(soc_decimal);
|
||||
|
||||
static ssize_t soc_decimal_rate_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = xm_get_soc_decimal_rate();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(soc_decimal_rate);
|
||||
|
||||
static ssize_t verify_digest_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
get_verify_digest(buf);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s", buf);
|
||||
}
|
||||
|
||||
static ssize_t verify_digest_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char kbuf[70] = { 0 };
|
||||
|
||||
pr_err("verify_digest_store = %s\n", buf);
|
||||
memset(kbuf, 0, sizeof(kbuf));
|
||||
strncpy(kbuf, buf, count - 1);
|
||||
set_verify_digest(kbuf);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(verify_digest);
|
||||
|
||||
static ssize_t chip_ok_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_CHIP_OK,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(chip_ok);
|
||||
|
||||
static ssize_t apdo_max_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
int apdo_max_volt, apdo_max_curr;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_APDO_VOLT_MAX,
|
||||
&apdo_max_volt);
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_APDO_CURR_MAX,
|
||||
&apdo_max_curr);
|
||||
val = (apdo_max_volt * apdo_max_curr) / 1000000;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(apdo_max);
|
||||
|
||||
static ssize_t fastchg_mode_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_FASTCHARGE_MODE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(fastchg_mode);
|
||||
|
||||
static ssize_t soh_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_SOH, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(soh);
|
||||
|
||||
static ssize_t connector_temp_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (!g_battmngr_iio->typec_conn_therm) {
|
||||
xm_get_iio_channel(g_battmngr_iio, "typec_conn_therm",
|
||||
&g_battmngr_iio->typec_conn_therm);
|
||||
}
|
||||
|
||||
if (g_battmngr_iio->typec_conn_therm) {
|
||||
iio_read_channel_processed(g_battmngr_iio->typec_conn_therm,
|
||||
&val);
|
||||
val /= 100;
|
||||
if (g_xm_charger->chg_feature->fake_conn_temp != 0)
|
||||
val = g_xm_charger->chg_feature->fake_conn_temp;
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
} else {
|
||||
pr_err("Failed to get IIO channel typec_conn_therm\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t connector_temp_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
g_xm_charger->chg_feature->fake_conn_temp = val;
|
||||
|
||||
pr_err("fake_conn_temp = %d\n",
|
||||
g_xm_charger->chg_feature->fake_conn_temp);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(connector_temp);
|
||||
|
||||
static ssize_t authentic_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_BATTERY_AUTH,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t authentic_store(struct class *c, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
xm_battmngr_write_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_BATTERY_AUTH, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(authentic);
|
||||
|
||||
static ssize_t cc_orientation_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY,
|
||||
PD_TYPEC_CC_ORIENTATION, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t cc_orientation_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
xm_battmngr_write_iio_prop(g_battmngr_iio, PD_PHY,
|
||||
PD_TYPEC_CC_ORIENTATION, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(cc_orientation);
|
||||
|
||||
static ssize_t typec_mode_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_TYPEC_MODE, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", get_typec_mode_name(val));
|
||||
}
|
||||
|
||||
static CLASS_ATTR_RO(typec_mode);
|
||||
|
||||
static ssize_t resistance_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static CLASS_ATTR_RO(resistance);
|
||||
|
||||
static ssize_t resistance_id_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_RESISTANCE_ID, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(resistance_id);
|
||||
|
||||
static ssize_t input_suspend_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (get_client_vote(g_xm_charger->input_suspend_votable,
|
||||
USER_VOTER) == 1);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t input_suspend_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
int rc;
|
||||
static int last_input_suspend;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
pr_err("input_suspend = %d\n", val);
|
||||
g_xm_charger->input_suspend = val;
|
||||
|
||||
if (g_xm_charger->input_suspend != last_input_suspend) {
|
||||
cancel_delayed_work(
|
||||
&(g_xm_charger->chg_feature->xm_prop_change_work));
|
||||
g_xm_charger->chg_feature->update_cont = 0;
|
||||
schedule_delayed_work(
|
||||
&(g_xm_charger->chg_feature->xm_prop_change_work), 0);
|
||||
last_input_suspend = g_xm_charger->input_suspend;
|
||||
}
|
||||
|
||||
rc = vote(g_xm_charger->input_suspend_votable, USER_VOTER, (bool)val,
|
||||
0);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(input_suspend);
|
||||
|
||||
static ssize_t usb_real_type_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int real_type = 0;
|
||||
|
||||
if (g_xm_charger->pd_active) {
|
||||
if (g_xm_charger->pd_active == QTI_POWER_SUPPLY_PD_ACTIVE)
|
||||
real_type = POWER_SUPPLY_USB_TYPE_PD;
|
||||
else if (g_xm_charger->pd_active ==
|
||||
QTI_POWER_SUPPLY_PD_PPS_ACTIVE)
|
||||
real_type = POWER_SUPPLY_USB_TYPE_PD_PPS;
|
||||
} else {
|
||||
real_type = g_xm_charger->bc12_type;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", get_usb_type_name(real_type));
|
||||
}
|
||||
static CLASS_ATTR_RO(usb_real_type);
|
||||
|
||||
static ssize_t adapter_id_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
uint32_t adapter_id;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, PD_PHY, PD_TYPEC_ADAPTER_ID,
|
||||
&adapter_id);
|
||||
pr_err("%s: adapter_id is %08x\n", __func__, adapter_id);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%08x\n", adapter_id);
|
||||
}
|
||||
static CLASS_ATTR_RO(adapter_id);
|
||||
|
||||
static ssize_t quick_charge_type_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = xm_get_quick_charge_type();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(quick_charge_type);
|
||||
|
||||
static ssize_t power_max_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = xm_get_adapter_power_max();
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(power_max);
|
||||
|
||||
static ssize_t shutdown_delay_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_SHUTDOWN_DELAY, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(shutdown_delay);
|
||||
|
||||
static ssize_t fg_temp_max_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TEMP_MAX,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(fg_temp_max);
|
||||
|
||||
static ssize_t fg_time_ot_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_TIME_OT,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(fg_time_ot);
|
||||
|
||||
static ssize_t fg_rsoc_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_RSOC, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(fg_rsoc);
|
||||
|
||||
static ssize_t fg_reg_rsoc_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG, BATT_FG_REG_ROC,
|
||||
&val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static CLASS_ATTR_RO(fg_reg_rsoc);
|
||||
|
||||
static ssize_t smart_batt_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = g_xm_charger->smartBatVal;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t smart_batt_store(struct class *c, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
int rc, mode;
|
||||
bool ffc_enable = 0;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
rc = xm_battmngr_read_iio_prop(g_battmngr_iio, BATT_FG,
|
||||
BATT_FG_FASTCHARGE_MODE, &mode);
|
||||
|
||||
ffc_enable = mode;
|
||||
|
||||
pr_err("Before process fv_profile:%d, non fv_profile:%d, ffc is: %d, Down value: %d.\n",
|
||||
g_xm_charger->dt.chg_voltage_max, g_xm_charger->dt.non_ffc_cv,
|
||||
ffc_enable, val);
|
||||
|
||||
if (g_xm_charger->dt.chg_voltage_max < 0 ||
|
||||
g_xm_charger->dt.non_ffc_cv < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val != 0) {
|
||||
if (!ffc_enable) {
|
||||
rc = vote(g_xm_charger->smart_batt_votable,
|
||||
SMART_BATTERY_FV, true, 0);
|
||||
rc = vote(g_xm_charger->fv_votable, SMART_BATTERY_FV,
|
||||
true,
|
||||
g_xm_charger->dt.non_ffc_cv - val * 1000);
|
||||
} else {
|
||||
rc = vote(g_xm_charger->fv_votable, SMART_BATTERY_FV,
|
||||
false, 0);
|
||||
rc = vote(g_xm_charger->smart_batt_votable,
|
||||
SMART_BATTERY_FV, true, val);
|
||||
}
|
||||
} else {
|
||||
if (!ffc_enable)
|
||||
rc = vote(g_xm_charger->fv_votable, SMART_BATTERY_FV,
|
||||
false, 0);
|
||||
else
|
||||
rc = vote(g_xm_charger->smart_batt_votable,
|
||||
SMART_BATTERY_FV, true, 0);
|
||||
}
|
||||
|
||||
g_xm_charger->smartBatVal = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(smart_batt);
|
||||
|
||||
static ssize_t night_charging_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = g_xm_charger->chg_feature->night_chg_flag;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t night_charging_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
g_xm_charger->chg_feature->night_chg_flag = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(night_charging);
|
||||
|
||||
static ssize_t battery_input_suspend_show(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (get_client_vote(g_xm_charger->fcc_votable, USER_VOTER) == 0);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t battery_input_suspend_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val, rc;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
rc = vote(g_xm_charger->fcc_votable, USER_VOTER, (bool)val, 0);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Couldn't vote to %s fcc rc=%d\n", __func__,
|
||||
(bool)val ? "suspend" : "resume", rc);
|
||||
}
|
||||
g_xm_charger->chg_feature->battery_input_suspend = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(battery_input_suspend);
|
||||
|
||||
static ssize_t set_icl_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_INPUT_CURRENT_SETTLED, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t set_icl_store(struct class *c, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val, rc;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
rc = xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_INPUT_CURRENT_SETTLED,
|
||||
val);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Couldn't set icl\n", __func__);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(set_icl);
|
||||
|
||||
static ssize_t vindpm_volt_show(struct class *c, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
xm_battmngr_read_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_INPUT_VOLTAGE_SETTLED, &val);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
static ssize_t vindpm_volt_store(struct class *c, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
xm_battmngr_write_iio_prop(g_battmngr_iio, MAIN_CHG,
|
||||
MAIN_CHARGER_INPUT_VOLTAGE_SETTLED, val);
|
||||
g_battmngr_noti->misc_msg.vindpm_temp = val;
|
||||
|
||||
pr_err("%s: g_battmngr_noti->misc_msg.vindpm:%d\n", __func__,
|
||||
g_battmngr_noti->misc_msg.vindpm_temp);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(vindpm_volt);
|
||||
|
||||
static ssize_t disable_thermal_show(struct class *c,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = g_battmngr_noti->misc_msg.disable_thermal;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t disable_thermal_store(struct class *c,
|
||||
struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val, rc;
|
||||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
g_battmngr_noti->misc_msg.disable_thermal = val;
|
||||
pr_err("%s: disable_thermal:%d\n", __func__,
|
||||
g_battmngr_noti->misc_msg.disable_thermal);
|
||||
|
||||
rc = xm_charger_thermal(g_xm_charger);
|
||||
if (rc < 0)
|
||||
pr_err("%s: Couldn't xm_charger_thermal\n", __func__);
|
||||
|
||||
return count;
|
||||
}
|
||||
static CLASS_ATTR_RW(disable_thermal);
|
||||
|
||||
static struct attribute *battmngr_class_attrs[] = {
|
||||
&class_attr_real_type.attr,
|
||||
&class_attr_pd_verifed.attr,
|
||||
&class_attr_input_current.attr,
|
||||
&class_attr_cp_ibus_slave.attr,
|
||||
&class_attr_cp_ibus_master.attr,
|
||||
&class_attr_cp_present_slave.attr,
|
||||
&class_attr_cp_present_master.attr,
|
||||
&class_attr_cp_vbus_voltage.attr,
|
||||
&class_attr_cp_vbat_voltage.attr,
|
||||
&class_attr_vbus_voltage.attr,
|
||||
&class_attr_vbat_voltage.attr,
|
||||
&class_attr_cell_voltage.attr,
|
||||
&class_attr_soc_decimal.attr,
|
||||
&class_attr_soc_decimal_rate.attr,
|
||||
&class_attr_verify_digest.attr,
|
||||
&class_attr_chip_ok.attr,
|
||||
&class_attr_apdo_max.attr,
|
||||
&class_attr_fastchg_mode.attr,
|
||||
&class_attr_soh.attr,
|
||||
&class_attr_connector_temp.attr,
|
||||
&class_attr_authentic.attr,
|
||||
&class_attr_cc_orientation.attr,
|
||||
&class_attr_typec_mode.attr,
|
||||
&class_attr_resistance.attr,
|
||||
&class_attr_resistance_id.attr,
|
||||
&class_attr_input_suspend.attr,
|
||||
&class_attr_usb_real_type.attr,
|
||||
&class_attr_adapter_id.attr,
|
||||
&class_attr_quick_charge_type.attr,
|
||||
&class_attr_power_max.attr,
|
||||
&class_attr_shutdown_delay.attr,
|
||||
&class_attr_fg_temp_max.attr,
|
||||
&class_attr_fg_time_ot.attr,
|
||||
&class_attr_cp_ibus_delta.attr,
|
||||
&class_attr_fg_rsoc.attr,
|
||||
&class_attr_smart_batt.attr,
|
||||
&class_attr_night_charging.attr,
|
||||
&class_attr_battery_input_suspend.attr,
|
||||
&class_attr_set_icl.attr,
|
||||
&class_attr_vindpm_volt.attr,
|
||||
&class_attr_disable_thermal.attr,
|
||||
&class_attr_fg_reg_rsoc.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(battmngr_class);
|
||||
|
||||
int battmngr_class_init(struct xm_battmngr *battmngr)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!battmngr)
|
||||
return -EINVAL;
|
||||
|
||||
battmngr->battmngr_class.name = "qcom-battery";
|
||||
battmngr->battmngr_class.class_groups = battmngr_class_groups;
|
||||
rc = class_register(&battmngr->battmngr_class);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to create battmngr_class rc=%d\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void battmngr_class_exit(struct xm_battmngr *battmngr)
|
||||
{
|
||||
class_destroy(&battmngr->battmngr_class);
|
||||
}
|
140
drivers/power/supply/xiaomi/battmngr/init/xm_battmngr_init.c
Normal file
140
drivers/power/supply/xiaomi/battmngr/init/xm_battmngr_init.c
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
#include "inc/xm_battmngr_init.h"
|
||||
|
||||
struct xm_battmngr *g_battmngr;
|
||||
EXPORT_SYMBOL(g_battmngr);
|
||||
|
||||
static int battmngr_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct battmngr_notify *noti_data = data;
|
||||
|
||||
pr_err("%s: event %d\n", __func__, event);
|
||||
|
||||
switch (event) {
|
||||
case BATTMNGR_EVENT_FG:
|
||||
battery_process_event_fg(noti_data);
|
||||
break;
|
||||
case BATTMNGR_EVENT_CP:
|
||||
charger_process_event_cp(noti_data);
|
||||
break;
|
||||
case BATTMNGR_EVENT_MAINCHG:
|
||||
charger_process_event_mainchg(noti_data);
|
||||
break;
|
||||
case BATTMNGR_EVENT_PD:
|
||||
charger_process_event_pd(noti_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int xm_battmngr_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct xm_battmngr *battmngr;
|
||||
int rc;
|
||||
|
||||
pr_err("%s: Start\n", __func__);
|
||||
|
||||
battmngr = devm_kzalloc(&pdev->dev, sizeof(*battmngr), GFP_KERNEL);
|
||||
if (!battmngr)
|
||||
return -ENOMEM;
|
||||
|
||||
battmngr->dev = &pdev->dev;
|
||||
battmngr->battmngr_iio.dev = &pdev->dev;
|
||||
battmngr->battery.dev = &pdev->dev;
|
||||
battmngr->charger.dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, battmngr);
|
||||
g_battmngr_iio = &battmngr->battmngr_iio;
|
||||
g_xm_battery = &battmngr->battery;
|
||||
g_xm_charger = &battmngr->charger;
|
||||
g_battmngr_noti = &battmngr->battmngr_noti;
|
||||
mutex_init(&g_battmngr_noti->notify_lock);
|
||||
|
||||
rc = xm_battmngr_iio_init(&battmngr->battmngr_iio);
|
||||
if (rc < 0) {
|
||||
pr_err("xm_battmngr_iio_init failed rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_battery_init(&battmngr->battery);
|
||||
if (rc < 0) {
|
||||
pr_err("xm_battery_init failed rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = xm_charger_init(&battmngr->charger);
|
||||
if (rc < 0) {
|
||||
pr_err("xm_charger_init failed rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = battmngr_class_init(battmngr);
|
||||
if (rc < 0) {
|
||||
pr_err("battmngr_class_init failed rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
battmngr->battmngr_nb.notifier_call = battmngr_notifier_call;
|
||||
rc = battmngr_notifier_register(&battmngr->battmngr_nb);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: register battmngr notifier fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
g_battmngr = battmngr;
|
||||
pr_err("%s: End\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_battmngr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xm_battmngr *battmngr = platform_get_drvdata(pdev);
|
||||
|
||||
xm_charger_deinit();
|
||||
battmngr_class_exit(battmngr);
|
||||
devm_kfree(&pdev->dev, battmngr);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xm_battmngr_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct of_device_id match_table[] = {
|
||||
{ .compatible = "xiaomi,battmngr" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver xm_battmngr_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "xm_battmngr",
|
||||
.of_match_table = match_table,
|
||||
},
|
||||
.probe = xm_battmngr_probe,
|
||||
.remove = xm_battmngr_remove,
|
||||
.shutdown = xm_battmngr_shutdown,
|
||||
};
|
||||
|
||||
static int __init xm_battmngr_init(void)
|
||||
{
|
||||
return platform_driver_register(&xm_battmngr_driver);
|
||||
}
|
||||
postcore_initcall(xm_battmngr_init);
|
||||
|
||||
static void __exit xm_battmngr_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&xm_battmngr_driver);
|
||||
}
|
||||
module_exit(xm_battmngr_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Xiaomi Battery Management");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
||||
MODULE_LICENSE("GPL v2");
|
4
drivers/power/supply/xiaomi/common/Makefile
Normal file
4
drivers/power/supply/xiaomi/common/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += battmngr_common.o
|
||||
battmngr_common-objs := battmngr_voter.o battmngr_notifier.o
|
31
drivers/power/supply/xiaomi/common/battmngr_notifier.c
Normal file
31
drivers/power/supply/xiaomi/common/battmngr_notifier.c
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(battmngr_notifier);
|
||||
|
||||
struct battmngr_notify *g_battmngr_noti;
|
||||
EXPORT_SYMBOL(g_battmngr_noti);
|
||||
|
||||
int battmngr_notifier_register(struct notifier_block *n)
|
||||
{
|
||||
return blocking_notifier_chain_register(&battmngr_notifier, n);
|
||||
}
|
||||
EXPORT_SYMBOL(battmngr_notifier_register);
|
||||
|
||||
int battmngr_notifier_unregister(struct notifier_block *n)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&battmngr_notifier, n);
|
||||
}
|
||||
EXPORT_SYMBOL(battmngr_notifier_unregister);
|
||||
|
||||
int battmngr_notifier_call_chain(unsigned long event,
|
||||
struct battmngr_notify *data)
|
||||
{
|
||||
return blocking_notifier_call_chain(&battmngr_notifier, event,
|
||||
(void *)data);
|
||||
}
|
||||
EXPORT_SYMBOL(battmngr_notifier_call_chain);
|
||||
|
||||
MODULE_DESCRIPTION("Battery Manager notifier");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
||||
MODULE_LICENSE("GPL v2");
|
820
drivers/power/supply/xiaomi/common/battmngr_voter.c
Normal file
820
drivers/power/supply/xiaomi/common/battmngr_voter.c
Normal file
@ -0,0 +1,820 @@
|
||||
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2015-2017, 2019-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/battmngr/battmngr_voter.h>
|
||||
|
||||
#define NUM_MAX_CLIENTS 32
|
||||
#define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT"
|
||||
|
||||
static DEFINE_SPINLOCK(votable_list_slock);
|
||||
static LIST_HEAD(votable_list);
|
||||
|
||||
static struct dentry *debug_root;
|
||||
|
||||
struct client_vote {
|
||||
bool enabled;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct votable {
|
||||
const char *name;
|
||||
const char *override_client;
|
||||
struct list_head list;
|
||||
struct client_vote votes[NUM_MAX_CLIENTS];
|
||||
int num_clients;
|
||||
int type;
|
||||
int effective_client_id;
|
||||
int effective_result;
|
||||
int override_result;
|
||||
struct mutex vote_lock;
|
||||
void *data;
|
||||
int (*callback)(struct votable *votable, void *data,
|
||||
int effective_result, const char *effective_client);
|
||||
char *client_strs[NUM_MAX_CLIENTS];
|
||||
bool voted_on;
|
||||
struct dentry *root;
|
||||
struct dentry *status_ent;
|
||||
u32 force_val;
|
||||
bool force_active;
|
||||
struct dentry *force_active_ent;
|
||||
};
|
||||
|
||||
/**
|
||||
* vote_set_any()
|
||||
* @votable: votable object
|
||||
* @client_id: client number of the latest voter
|
||||
* @eff_res: sets 0 or 1 based on the voting
|
||||
* @eff_id: Always returns the client_id argument
|
||||
*
|
||||
* Note that for SET_ANY voter, the value is always same as enabled. There is
|
||||
* no idea of a voter abstaining from the election. Hence there is never a
|
||||
* situation when the effective_id will be invalid, during election.
|
||||
*
|
||||
* Context:
|
||||
* Must be called with the votable->lock held
|
||||
*/
|
||||
static void vote_set_any(struct votable *votable, int client_id, int *eff_res,
|
||||
int *eff_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
*eff_res = 0;
|
||||
|
||||
for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
|
||||
*eff_res |= votable->votes[i].enabled;
|
||||
|
||||
*eff_id = client_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* vote_min() -
|
||||
* @votable: votable object
|
||||
* @client_id: client number of the latest voter
|
||||
* @eff_res: sets this to the min. of all the values amongst enabled voters.
|
||||
* If there is no enabled client, this is set to INT_MAX
|
||||
* @eff_id: sets this to the client id that has the min value amongst all
|
||||
* the enabled clients. If there is no enabled client, sets this
|
||||
* to -EINVAL
|
||||
*
|
||||
* Context:
|
||||
* Must be called with the votable->lock held
|
||||
*/
|
||||
static void vote_min(struct votable *votable, int client_id, int *eff_res,
|
||||
int *eff_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
*eff_res = INT_MAX;
|
||||
*eff_id = -EINVAL;
|
||||
for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
|
||||
if (votable->votes[i].enabled &&
|
||||
*eff_res > votable->votes[i].value) {
|
||||
*eff_res = votable->votes[i].value;
|
||||
*eff_id = i;
|
||||
}
|
||||
}
|
||||
if (*eff_id == -EINVAL)
|
||||
*eff_res = -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* vote_max() -
|
||||
* @votable: votable object
|
||||
* @client_id: client number of the latest voter
|
||||
* @eff_res: sets this to the max. of all the values amongst enabled voters.
|
||||
* If there is no enabled client, this is set to -EINVAL
|
||||
* @eff_id: sets this to the client id that has the max value amongst all
|
||||
* the enabled clients. If there is no enabled client, sets this to
|
||||
* -EINVAL
|
||||
*
|
||||
* Context:
|
||||
* Must be called with the votable->lock held
|
||||
*/
|
||||
static void vote_max(struct votable *votable, int client_id, int *eff_res,
|
||||
int *eff_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
*eff_res = INT_MIN;
|
||||
*eff_id = -EINVAL;
|
||||
for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
|
||||
if (votable->votes[i].enabled &&
|
||||
*eff_res < votable->votes[i].value) {
|
||||
*eff_res = votable->votes[i].value;
|
||||
*eff_id = i;
|
||||
}
|
||||
}
|
||||
if (*eff_id == -EINVAL)
|
||||
*eff_res = -EINVAL;
|
||||
}
|
||||
|
||||
static int get_client_id(struct votable *votable, const char *client_str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < votable->num_clients; i++) {
|
||||
if (votable->client_strs[i] &&
|
||||
(strcmp(votable->client_strs[i], client_str) == 0))
|
||||
return i;
|
||||
}
|
||||
|
||||
/* new client */
|
||||
for (i = 0; i < votable->num_clients; i++) {
|
||||
if (!votable->client_strs[i]) {
|
||||
votable->client_strs[i] =
|
||||
kstrdup(client_str, GFP_KERNEL);
|
||||
if (!votable->client_strs[i])
|
||||
return -ENOMEM;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static char *get_client_str(struct votable *votable, int client_id)
|
||||
{
|
||||
if (!votable || (client_id == -EINVAL))
|
||||
return NULL;
|
||||
|
||||
return votable->client_strs[client_id];
|
||||
}
|
||||
|
||||
void lock_votable(struct votable *votable)
|
||||
{
|
||||
mutex_lock(&votable->vote_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(lock_votable);
|
||||
|
||||
void unlock_votable(struct votable *votable)
|
||||
{
|
||||
mutex_unlock(&votable->vote_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(unlock_votable);
|
||||
|
||||
/**
|
||||
* is_override_vote_enabled() -
|
||||
* is_override_vote_enabled_locked() -
|
||||
* The unlocked and locked variants of getting whether override
|
||||
vote is enabled.
|
||||
* @votable: the votable object
|
||||
*
|
||||
* Returns:
|
||||
* True if the client's vote is enabled; false otherwise.
|
||||
*/
|
||||
bool is_override_vote_enabled_locked(struct votable *votable)
|
||||
{
|
||||
if (!votable)
|
||||
return false;
|
||||
|
||||
return votable->override_result != -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(is_override_vote_enabled_locked);
|
||||
|
||||
bool is_override_vote_enabled(struct votable *votable)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
if (!votable)
|
||||
return false;
|
||||
|
||||
lock_votable(votable);
|
||||
enable = is_override_vote_enabled_locked(votable);
|
||||
unlock_votable(votable);
|
||||
|
||||
return enable;
|
||||
}
|
||||
EXPORT_SYMBOL(is_override_vote_enabled);
|
||||
|
||||
/**
|
||||
* is_client_vote_enabled() -
|
||||
* is_client_vote_enabled_locked() -
|
||||
* The unlocked and locked variants of getting whether a client's
|
||||
vote is enabled.
|
||||
* @votable: the votable object
|
||||
* @client_str: client of interest
|
||||
*
|
||||
* Returns:
|
||||
* True if the client's vote is enabled; false otherwise.
|
||||
*/
|
||||
bool is_client_vote_enabled_locked(struct votable *votable,
|
||||
const char *client_str)
|
||||
{
|
||||
int client_id;
|
||||
|
||||
if (!votable || !client_str)
|
||||
return false;
|
||||
|
||||
client_id = get_client_id(votable, client_str);
|
||||
if (client_id < 0)
|
||||
return false;
|
||||
|
||||
return votable->votes[client_id].enabled;
|
||||
}
|
||||
EXPORT_SYMBOL(is_client_vote_enabled_locked);
|
||||
|
||||
bool is_client_vote_enabled(struct votable *votable, const char *client_str)
|
||||
{
|
||||
bool enabled;
|
||||
|
||||
if (!votable || !client_str)
|
||||
return false;
|
||||
|
||||
lock_votable(votable);
|
||||
enabled = is_client_vote_enabled_locked(votable, client_str);
|
||||
unlock_votable(votable);
|
||||
return enabled;
|
||||
}
|
||||
EXPORT_SYMBOL(is_client_vote_enabled);
|
||||
|
||||
/**
|
||||
* get_client_vote() -
|
||||
* get_client_vote_locked() -
|
||||
* The unlocked and locked variants of getting a client's voted
|
||||
* value.
|
||||
* @votable: the votable object
|
||||
* @client_str: client of interest
|
||||
*
|
||||
* Returns:
|
||||
* The value the client voted for. -EINVAL is returned if the client
|
||||
* is not enabled or the client is not found.
|
||||
*/
|
||||
int get_client_vote_locked(struct votable *votable, const char *client_str)
|
||||
{
|
||||
int client_id;
|
||||
|
||||
if (!votable || !client_str)
|
||||
return -EINVAL;
|
||||
|
||||
client_id = get_client_id(votable, client_str);
|
||||
if (client_id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if ((votable->type != VOTE_SET_ANY) &&
|
||||
!votable->votes[client_id].enabled)
|
||||
return -EINVAL;
|
||||
|
||||
return votable->votes[client_id].value;
|
||||
}
|
||||
EXPORT_SYMBOL(get_client_vote_locked);
|
||||
|
||||
int get_client_vote(struct votable *votable, const char *client_str)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (!votable || !client_str)
|
||||
return -EINVAL;
|
||||
|
||||
lock_votable(votable);
|
||||
value = get_client_vote_locked(votable, client_str);
|
||||
unlock_votable(votable);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL(get_client_vote);
|
||||
|
||||
/**
|
||||
* get_effective_result() -
|
||||
* get_effective_result_locked() -
|
||||
* The unlocked and locked variants of getting the effective value
|
||||
* amongst all the enabled voters.
|
||||
*
|
||||
* @votable: the votable object
|
||||
*
|
||||
* Returns:
|
||||
* The effective result.
|
||||
* For MIN and MAX votable, returns -EINVAL when the votable
|
||||
* object has been created but no clients have casted their votes or
|
||||
* the last enabled client disables its vote.
|
||||
* For SET_ANY votable it returns 0 when no clients have casted their votes
|
||||
* because for SET_ANY there is no concept of abstaining from election. The
|
||||
* votes for all the clients of SET_ANY votable is defaulted to false.
|
||||
*/
|
||||
int get_effective_result_locked(struct votable *votable)
|
||||
{
|
||||
if (!votable)
|
||||
return -EINVAL;
|
||||
|
||||
if (votable->force_active)
|
||||
return votable->force_val;
|
||||
|
||||
if (votable->override_result != -EINVAL)
|
||||
return votable->override_result;
|
||||
|
||||
return votable->effective_result;
|
||||
}
|
||||
EXPORT_SYMBOL(get_effective_result_locked);
|
||||
|
||||
int get_effective_result(struct votable *votable)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (!votable)
|
||||
return -EINVAL;
|
||||
|
||||
lock_votable(votable);
|
||||
value = get_effective_result_locked(votable);
|
||||
unlock_votable(votable);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL(get_effective_result);
|
||||
|
||||
/**
|
||||
* get_effective_client() -
|
||||
* get_effective_client_locked() -
|
||||
* The unlocked and locked variants of getting the effective client
|
||||
* amongst all the enabled voters.
|
||||
*
|
||||
* @votable: the votable object
|
||||
*
|
||||
* Returns:
|
||||
* The effective client.
|
||||
* For MIN and MAX votable, returns NULL when the votable
|
||||
* object has been created but no clients have casted their votes or
|
||||
* the last enabled client disables its vote.
|
||||
* For SET_ANY votable it returns NULL too when no clients have casted
|
||||
* their votes. But for SET_ANY since there is no concept of abstaining
|
||||
* from election, the only client that casts a vote or the client that
|
||||
* caused the result to change is returned.
|
||||
*/
|
||||
const char *get_effective_client_locked(struct votable *votable)
|
||||
{
|
||||
if (!votable)
|
||||
return NULL;
|
||||
|
||||
if (votable->force_active)
|
||||
return DEBUG_FORCE_CLIENT;
|
||||
|
||||
if (votable->override_result != -EINVAL)
|
||||
return votable->override_client;
|
||||
|
||||
return get_client_str(votable, votable->effective_client_id);
|
||||
}
|
||||
EXPORT_SYMBOL(get_effective_client_locked);
|
||||
|
||||
const char *get_effective_client(struct votable *votable)
|
||||
{
|
||||
const char *client_str;
|
||||
|
||||
if (!votable)
|
||||
return NULL;
|
||||
|
||||
lock_votable(votable);
|
||||
client_str = get_effective_client_locked(votable);
|
||||
unlock_votable(votable);
|
||||
return client_str;
|
||||
}
|
||||
EXPORT_SYMBOL(get_effective_client);
|
||||
|
||||
/**
|
||||
* vote() -
|
||||
*
|
||||
* @votable: the votable object
|
||||
* @client_str: the voting client
|
||||
* @enabled: This provides a means for the client to exclude himself from
|
||||
* election. This clients val (the next argument) will be
|
||||
* considered only when he has enabled his participation.
|
||||
* Note that this takes a differnt meaning for SET_ANY type, as
|
||||
* there is no concept of abstaining from participation.
|
||||
* Enabled is treated as the boolean value the client is voting.
|
||||
* @val: The vote value. This is ignored for SET_ANY votable types.
|
||||
* For MIN, MAX votable types this value is used as the
|
||||
* clients vote value when the enabled is true, this value is
|
||||
* ignored if enabled is false.
|
||||
*
|
||||
* The callback is called only when there is a change in the election results or
|
||||
* if it is the first time someone is voting.
|
||||
*
|
||||
* Returns:
|
||||
* The return from the callback when present and needs to be called
|
||||
* or zero.
|
||||
*/
|
||||
int vote(struct votable *votable, const char *client_str, bool enabled, int val)
|
||||
{
|
||||
int effective_id = -EINVAL;
|
||||
int effective_result;
|
||||
int client_id;
|
||||
int rc = 0;
|
||||
bool similar_vote = false;
|
||||
|
||||
if (!votable || !client_str)
|
||||
return -EINVAL;
|
||||
|
||||
lock_votable(votable);
|
||||
|
||||
client_id = get_client_id(votable, client_str);
|
||||
if (client_id < 0) {
|
||||
rc = client_id;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* for SET_ANY the val is to be ignored, set it
|
||||
* to enabled so that the election still works based on
|
||||
* value regardless of the type
|
||||
*/
|
||||
if (votable->type == VOTE_SET_ANY)
|
||||
val = enabled;
|
||||
|
||||
if ((votable->votes[client_id].enabled == enabled) &&
|
||||
(votable->votes[client_id].value == val)) {
|
||||
pr_err("%s: %s,%d same vote %s of val=%d\n", votable->name,
|
||||
client_str, client_id, enabled ? "on" : "off", val);
|
||||
similar_vote = true;
|
||||
}
|
||||
|
||||
votable->votes[client_id].enabled = enabled;
|
||||
votable->votes[client_id].value = val;
|
||||
|
||||
if (similar_vote && votable->voted_on) {
|
||||
pr_err("%s: %s,%d Ignoring similar vote %s of val=%d\n",
|
||||
votable->name, client_str, client_id,
|
||||
enabled ? "on" : "off", val);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_err("%s: %s,%d voting %s of val=%d\n", votable->name, client_str,
|
||||
client_id, enabled ? "on" : "off", val);
|
||||
switch (votable->type) {
|
||||
case VOTE_MIN:
|
||||
vote_min(votable, client_id, &effective_result, &effective_id);
|
||||
break;
|
||||
case VOTE_MAX:
|
||||
vote_max(votable, client_id, &effective_result, &effective_id);
|
||||
break;
|
||||
case VOTE_SET_ANY:
|
||||
vote_set_any(votable, client_id, &effective_result,
|
||||
&effective_id);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that the callback is called with a NULL string and -EINVAL
|
||||
* result when there are no enabled votes
|
||||
*/
|
||||
if (!votable->voted_on ||
|
||||
(effective_result != votable->effective_result)) {
|
||||
votable->effective_client_id = effective_id;
|
||||
votable->effective_result = effective_result;
|
||||
pr_err("%s: effective vote is now %d voted by %s,%d\n",
|
||||
votable->name, effective_result,
|
||||
get_client_str(votable, effective_id), effective_id);
|
||||
if (votable->callback && !votable->force_active &&
|
||||
(votable->override_result == -EINVAL))
|
||||
rc = votable->callback(
|
||||
votable, votable->data, effective_result,
|
||||
get_client_str(votable, effective_id));
|
||||
}
|
||||
|
||||
votable->voted_on = true;
|
||||
out:
|
||||
unlock_votable(votable);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(vote);
|
||||
|
||||
/**
|
||||
* vote_override() -
|
||||
*
|
||||
* @votable: The votable object
|
||||
* @override_client: The voting client that will override other client's
|
||||
* votes, that are already present. When force_active
|
||||
* and override votes are set on a votable, force_active's
|
||||
* client will have the higher priority and it's vote will
|
||||
* be the effective one.
|
||||
* @enabled: This provides a means for the override client to exclude
|
||||
* itself from election. This client's vote
|
||||
* (the next argument) will be considered only when
|
||||
* it has enabled its participation. When this is
|
||||
* set true, this will force a value on a MIN/MAX votable
|
||||
* irrespective of its current value.
|
||||
* @val: The vote value. This will be effective only if enabled
|
||||
* is set true.
|
||||
* Returns:
|
||||
* The result of vote. 0 is returned if the vote
|
||||
* is successfully set by the overriding client, when enabled is set.
|
||||
*/
|
||||
int vote_override(struct votable *votable, const char *override_client,
|
||||
bool enabled, int val)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!votable || !override_client)
|
||||
return -EINVAL;
|
||||
|
||||
lock_votable(votable);
|
||||
if (votable->force_active) {
|
||||
votable->override_result = enabled ? val : -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
rc = votable->callback(votable, votable->data, val,
|
||||
override_client);
|
||||
if (!rc) {
|
||||
votable->override_client = override_client;
|
||||
votable->override_result = val;
|
||||
}
|
||||
} else {
|
||||
rc = votable->callback(
|
||||
votable, votable->data, votable->effective_result,
|
||||
get_client_str(votable, votable->effective_client_id));
|
||||
votable->override_result = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
unlock_votable(votable);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(vote_override);
|
||||
|
||||
int rerun_election(struct votable *votable)
|
||||
{
|
||||
int rc = 0;
|
||||
int effective_result;
|
||||
|
||||
if (!votable)
|
||||
return -EINVAL;
|
||||
|
||||
lock_votable(votable);
|
||||
effective_result = get_effective_result_locked(votable);
|
||||
if (votable->callback)
|
||||
rc = votable->callback(
|
||||
votable, votable->data, effective_result,
|
||||
get_client_str(votable, votable->effective_client_id));
|
||||
unlock_votable(votable);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(rerun_election);
|
||||
|
||||
struct votable *find_votable(const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct votable *v;
|
||||
bool found = false;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&votable_list_slock, flags);
|
||||
if (list_empty(&votable_list))
|
||||
goto out;
|
||||
|
||||
list_for_each_entry (v, &votable_list, list) {
|
||||
if (strcmp(v->name, name) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(&votable_list_slock, flags);
|
||||
|
||||
if (found)
|
||||
return v;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(find_votable);
|
||||
|
||||
static int force_active_get(void *data, u64 *val)
|
||||
{
|
||||
struct votable *votable = data;
|
||||
|
||||
*val = votable->force_active;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int force_active_set(void *data, u64 val)
|
||||
{
|
||||
struct votable *votable = data;
|
||||
int rc = 0;
|
||||
int effective_result;
|
||||
const char *client;
|
||||
|
||||
lock_votable(votable);
|
||||
votable->force_active = !!val;
|
||||
|
||||
if (!votable->callback)
|
||||
goto out;
|
||||
|
||||
if (votable->force_active) {
|
||||
rc = votable->callback(votable, votable->data,
|
||||
votable->force_val, DEBUG_FORCE_CLIENT);
|
||||
} else {
|
||||
if (votable->override_result != -EINVAL) {
|
||||
effective_result = votable->override_result;
|
||||
client = votable->override_client;
|
||||
} else {
|
||||
effective_result = votable->effective_result;
|
||||
client = get_client_str(votable,
|
||||
votable->effective_client_id);
|
||||
}
|
||||
rc = votable->callback(votable, votable->data, effective_result,
|
||||
client);
|
||||
}
|
||||
out:
|
||||
unlock_votable(votable);
|
||||
return rc;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(votable_force_ops, force_active_get, force_active_set,
|
||||
"%lld\n");
|
||||
|
||||
static int show_votable_clients(struct seq_file *m, void *data)
|
||||
{
|
||||
struct votable *votable = m->private;
|
||||
int i;
|
||||
char *type_str = "Unkonwn";
|
||||
const char *effective_client_str;
|
||||
|
||||
lock_votable(votable);
|
||||
|
||||
for (i = 0; i < votable->num_clients; i++) {
|
||||
if (votable->client_strs[i]) {
|
||||
seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n",
|
||||
votable->name, votable->client_strs[i],
|
||||
votable->votes[i].enabled,
|
||||
votable->votes[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
switch (votable->type) {
|
||||
case VOTE_MIN:
|
||||
type_str = "Min";
|
||||
break;
|
||||
case VOTE_MAX:
|
||||
type_str = "Max";
|
||||
break;
|
||||
case VOTE_SET_ANY:
|
||||
type_str = "Set_any";
|
||||
break;
|
||||
}
|
||||
|
||||
effective_client_str = get_effective_client_locked(votable);
|
||||
seq_printf(m, "%s: effective=%s type=%s v=%d\n", votable->name,
|
||||
effective_client_str ? effective_client_str : "none",
|
||||
type_str, get_effective_result_locked(votable));
|
||||
unlock_votable(votable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int votable_status_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct votable *votable = inode->i_private;
|
||||
|
||||
return single_open(file, show_votable_clients, votable);
|
||||
}
|
||||
|
||||
static const struct file_operations votable_status_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = votable_status_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
struct votable *create_votable(const char *name, int votable_type,
|
||||
int (*callback)(struct votable *votable,
|
||||
void *data, int effective_result,
|
||||
const char *effective_client),
|
||||
void *data)
|
||||
{
|
||||
struct votable *votable;
|
||||
unsigned long flags;
|
||||
|
||||
if (!name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
votable = find_votable(name);
|
||||
if (votable)
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
if (debug_root == NULL) {
|
||||
debug_root = debugfs_create_dir("battmngr-votable", NULL);
|
||||
if (!debug_root) {
|
||||
pr_err("Couldn't create debug dir\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
if (votable_type >= NUM_VOTABLE_TYPES) {
|
||||
pr_err("Invalid votable_type specified for voter\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
votable = kzalloc(sizeof(struct votable), GFP_KERNEL);
|
||||
if (!votable)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
votable->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!votable->name) {
|
||||
kfree(votable);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
votable->num_clients = NUM_MAX_CLIENTS;
|
||||
votable->callback = callback;
|
||||
votable->type = votable_type;
|
||||
votable->data = data;
|
||||
votable->override_result = -EINVAL;
|
||||
mutex_init(&votable->vote_lock);
|
||||
|
||||
/*
|
||||
* Because effective_result and client states are invalid
|
||||
* before the first vote, initialize them to -EINVAL
|
||||
*/
|
||||
votable->effective_result = -EINVAL;
|
||||
if (votable->type == VOTE_SET_ANY)
|
||||
votable->effective_result = 0;
|
||||
votable->effective_client_id = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&votable_list_slock, flags);
|
||||
list_add(&votable->list, &votable_list);
|
||||
spin_unlock_irqrestore(&votable_list_slock, flags);
|
||||
|
||||
votable->root = debugfs_create_dir(name, debug_root);
|
||||
if (!votable->root) {
|
||||
pr_err("Couldn't create debug dir %s\n", name);
|
||||
kfree(votable->name);
|
||||
kfree(votable);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
votable->status_ent =
|
||||
debugfs_create_file("status", S_IFREG | 0444, votable->root,
|
||||
votable, &votable_status_ops);
|
||||
if (!votable->status_ent) {
|
||||
pr_err("Couldn't create status dbg file for %s\n", name);
|
||||
debugfs_remove_recursive(votable->root);
|
||||
kfree(votable->name);
|
||||
kfree(votable);
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
|
||||
debugfs_create_u32("force_val", S_IFREG | 0644, votable->root,
|
||||
&(votable->force_val));
|
||||
|
||||
votable->force_active_ent =
|
||||
debugfs_create_file("force_active", S_IFREG | 0444,
|
||||
votable->root, votable, &votable_force_ops);
|
||||
if (!votable->force_active_ent) {
|
||||
pr_err("Couldn't create force_active dbg file for %s\n", name);
|
||||
debugfs_remove_recursive(votable->root);
|
||||
kfree(votable->name);
|
||||
kfree(votable);
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
|
||||
return votable;
|
||||
}
|
||||
EXPORT_SYMBOL(create_votable);
|
||||
|
||||
void destroy_votable(struct votable *votable)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (!votable)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&votable_list_slock, flags);
|
||||
list_del(&votable->list);
|
||||
spin_unlock_irqrestore(&votable_list_slock, flags);
|
||||
|
||||
debugfs_remove_recursive(votable->root);
|
||||
|
||||
for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
|
||||
kfree(votable->client_strs[i]);
|
||||
|
||||
kfree(votable->name);
|
||||
kfree(votable);
|
||||
}
|
||||
EXPORT_SYMBOL(destroy_votable);
|
||||
|
||||
MODULE_DESCRIPTION("Battery Manager Voter");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
5
drivers/power/supply/xiaomi/platform/Makefile
Normal file
5
drivers/power/supply/xiaomi/platform/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += external/
|
||||
obj-$(CONFIG_MTK_POWER_SUPPLY) += mediatek/
|
||||
obj-$(CONFIG_QCOM_POWER_SUPPLY) += qualcomm/
|
6
drivers/power/supply/xiaomi/platform/external/Makefile
vendored
Normal file
6
drivers/power/supply/xiaomi/platform/external/Makefile
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += cp/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += fg/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += mainchg/
|
||||
obj-$(CONFIG_XM_POWER_SUPPLY) += pd/
|
7
drivers/power/supply/xiaomi/platform/external/cp/Makefile
vendored
Normal file
7
drivers/power/supply/xiaomi/platform/external/cp/Makefile
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CHARGER_PUMP_SC8551A) += sc8551a_drv.o
|
||||
sc8551a_drv-objs := sc8551a.o sc8551a_iio.o
|
||||
|
||||
obj-$(CONFIG_CHARGER_PUMP_LN8000) += ln8000_drv.o
|
||||
ln8000_drv-objs := ln8000.o ln8000_iio.o
|
262
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000.h
vendored
Normal file
262
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000.h
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
|
||||
#ifndef __LN8000_H__
|
||||
#define __LN8000_H__
|
||||
|
||||
/*
|
||||
* ln8000-charger.c - Charger driver for LIONSEMI LN8000
|
||||
*
|
||||
* Copyright (C) 2021 Lion Semiconductor Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
|
||||
static const char *ln8000_dev_name[] = {
|
||||
"ln8000-standalone",
|
||||
"ln8000-master",
|
||||
"ln8000-slave",
|
||||
};
|
||||
|
||||
#define LN8000_ROLE_STDALONE 0
|
||||
#define LN8000_ROLE_SLAVE 1
|
||||
#define LN8000_ROLE_MASTER 2
|
||||
|
||||
enum {
|
||||
LN8000_STDALONE,
|
||||
LN8000_SLAVE,
|
||||
LN8000_MASTER,
|
||||
};
|
||||
|
||||
#define PROBE_CNT_MAX 50
|
||||
|
||||
static int ln8000_mode_data[] = {
|
||||
[LN8000_STDALONE] = LN8000_ROLE_STDALONE,
|
||||
[LN8000_MASTER] = LN8000_ROLE_SLAVE,
|
||||
[LN8000_SLAVE] = LN8000_ROLE_MASTER,
|
||||
};
|
||||
|
||||
#define ln_err(fmt, ...) \
|
||||
do { \
|
||||
if (info->dev_role == LN_ROLE_STANDALONE) \
|
||||
printk(KERN_ERR "ln8000-standalone: %s: " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
else if (info->dev_role == LN_ROLE_MASTER) \
|
||||
printk(KERN_ERR "ln8000-master: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_ERR "ln8000-slave: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define ln_info(fmt, ...) \
|
||||
do { \
|
||||
if (info->dev_role == LN_ROLE_STANDALONE) \
|
||||
printk(KERN_INFO "ln8000-standalone: %s: " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
else if (info->dev_role == LN_ROLE_MASTER) \
|
||||
printk(KERN_INFO "ln8000-master: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_INFO "ln8000-slave: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define ln_dbg(fmt, ...) \
|
||||
do { \
|
||||
if (info->dev_role == LN_ROLE_STANDALONE) \
|
||||
printk(KERN_DEBUG "ln8000-standalone: %s: " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
else if (info->dev_role == LN_ROLE_MASTER) \
|
||||
printk(KERN_DEBUG "ln8000-master: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_DEBUG "ln8000-slave: %s: " fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define LN8000_REG_PRINT(reg_addr, val) \
|
||||
do { \
|
||||
ln_info(" --> [%-20s] 0x%02X : 0x%02X\n", #reg_addr, \
|
||||
LN8000_REG_##reg_addr, (val) & 0xFF); \
|
||||
} while (0);
|
||||
|
||||
#define LN8000_PARSE_PROP(ret, pdata, field, prop, default_prop) \
|
||||
do { \
|
||||
if (ret) { \
|
||||
ln_info("%s = %d (set to default)\n", #field, \
|
||||
default_prop); \
|
||||
pdata->field = default_prop; \
|
||||
} else { \
|
||||
ln_info("%s = %d\n", #field, prop); \
|
||||
pdata->field = prop; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define LN8000_BIT_CHECK(val, idx, desc) \
|
||||
if (val & (1 << idx)) \
|
||||
ln_info("-> %s\n", desc)
|
||||
#define LN8000_USE_GPIO(pdata) \
|
||||
((pdata != NULL) && (!IS_ERR_OR_NULL(pdata->irq_gpio)))
|
||||
#define LN8000_STATUS(val, mask) ((val & mask) ? true : false)
|
||||
|
||||
enum {
|
||||
VBUS_ERROR_NONE,
|
||||
VBUS_ERROR_LOW,
|
||||
VBUS_ERROR_HIGHT,
|
||||
};
|
||||
|
||||
/**
|
||||
* driver instance structure definition
|
||||
*/
|
||||
struct ln8000_platform_data {
|
||||
struct gpio_desc
|
||||
*irq_gpio; /* GPIO pin for (generic/power-on) interrupt */
|
||||
|
||||
/* feature configuration */
|
||||
unsigned int bat_ovp_th; /* battery ovp threshold (mV) */
|
||||
unsigned int bat_ovp_alarm_th; /* battery ovp alarm threshold (mV) */
|
||||
unsigned int bus_ovp_th; /* IIN ovp threshold (mV) */
|
||||
unsigned int bus_ovp_alarm_th; /* IIN ovp alarm threshold (mV) */
|
||||
unsigned int bus_ocp_th; /* IIN ocp threshold (mA) */
|
||||
unsigned int bus_ocp_alarm_th; /* IIN ocp alarm threshold */
|
||||
unsigned int
|
||||
ntc_alarm_cfg; /* input/battery NTC voltage threshold code: 0~1023 */
|
||||
|
||||
/* protection enable/disable */
|
||||
bool vbat_ovp_disable; /* disable battery voltage OVP */
|
||||
bool vbat_reg_disable; /* disable battery voltage (float) regulation */
|
||||
bool iin_ocp_disable; /* disable input current OCP */
|
||||
bool iin_reg_disable; /* disable input current regulation */
|
||||
bool tbus_mon_disable; /* disable BUS temperature monitor (prot/alarm) */
|
||||
bool tbat_mon_disable; /* disable BAT temperature monitor (prot/alarm) */
|
||||
bool tdie_prot_disable; /* disable die temperature protection */
|
||||
bool tdie_reg_disable; /* disable die temperature regulation */
|
||||
bool revcurr_prot_disable; /* disable reverse current protection */
|
||||
};
|
||||
|
||||
struct ln8000_info {
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct ln8000_platform_data *pdata;
|
||||
struct mutex irq_complete;
|
||||
|
||||
struct mutex data_lock;
|
||||
struct mutex i2c_lock;
|
||||
struct mutex irq_lock;
|
||||
|
||||
unsigned int op_mode; /* target operation mode */
|
||||
unsigned int pwr_status; /* current device status */
|
||||
unsigned int dev_role; /* device role */
|
||||
|
||||
/* system/device status */
|
||||
bool vbat_regulated; /* vbat loop is active (+ OV alarm) */
|
||||
bool iin_regulated; /* iin loop is active (+ OC alarm) */
|
||||
bool tdie_fault; /* die temperature fault */
|
||||
bool tbus_tbat_fault; /* BUS/BAT temperature fault */
|
||||
bool tdie_alarm; /* die temperature alarm (regulated) */
|
||||
bool tbus_tbat_alarm; /* BUS/BAT temperature alarm */
|
||||
bool wdt_fault; /* watchdog timer expiration */
|
||||
bool vbat_ov; /* vbat OV fault */
|
||||
bool vac_ov; /* vac OV fault */
|
||||
bool vbus_ov; /* vbus OV fault */
|
||||
bool iin_oc; /* iin OC fault */
|
||||
bool vac_unplug; /* vac unplugged */ //vbus_present
|
||||
bool iin_rc; /* iin reverse current detected */
|
||||
bool volt_qual; /* all voltages are qualified */
|
||||
bool usb_present; /* usb plugged (present) */
|
||||
bool batt_present;
|
||||
bool chg_en; /* charging enavbled */
|
||||
bool rcp_en; /* reverse current protection enabled */
|
||||
int vbat_ovp_alarm_th; /* vbat ovp alarm threshold */
|
||||
int vin_ovp_alarm_th; /* vin ovp alarm threshold */
|
||||
int iin_ocp_alarm_th; /* iin ocp alarm threshold */
|
||||
|
||||
/* ADC readings */
|
||||
int tbat_uV; /* BAT temperature (NTC, uV) */
|
||||
int tbus_uV; /* BUS temperature (NTC, uV) */
|
||||
int tdie_dC; /* die temperature (deci-Celsius) */
|
||||
int vbat_uV; /* battery voltage (uV) */
|
||||
int vbus_uV; /* input voltage (uV) */
|
||||
int iin_uA; /* input current (uV) */
|
||||
|
||||
/* for restore reg_init_val */
|
||||
u8 regulation_ctrl;
|
||||
u8 adc_ctrl;
|
||||
u8 v_float_ctrl;
|
||||
u8 charge_ctrl;
|
||||
|
||||
/* debugfs */
|
||||
struct dentry *debug_root;
|
||||
u32 debug_address;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct iio_channel *int_iio_chans;
|
||||
struct delayed_work dump_regs_work;
|
||||
};
|
||||
|
||||
int ln8000_set_sw_freq(struct ln8000_info *info, u8 fsw_cfg);
|
||||
int ln8000_set_vac_ovp(struct ln8000_info *info, unsigned int ovp_th);
|
||||
int ln8000_set_vbat_float(struct ln8000_info *info, unsigned int cfg);
|
||||
int ln8000_set_iin_limit(struct ln8000_info *info, unsigned int cfg);
|
||||
int ln8000_set_ntc_alarm(struct ln8000_info *info, unsigned int cfg);
|
||||
int ln8000_enable_vbat_ovp(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_vbat_regulation(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_vbat_loop_int(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_iin_ocp(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_iin_regulation(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_iin_loop_int(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_tdie_prot(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_tdie_regulation(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_tbus_monitor(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_tbat_monitor(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_wdt(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_rcp(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_auto_recovery(struct ln8000_info *info, bool enable);
|
||||
int ln8000_enable_rcp_auto_recovery(struct ln8000_info *info, bool enable);
|
||||
int ln8000_set_adc_mode(struct ln8000_info *info, unsigned int cfg);
|
||||
int ln8000_set_adc_hib_delay(struct ln8000_info *info, unsigned int cfg);
|
||||
int ln8000_check_status(struct ln8000_info *info);
|
||||
void ln8000_soft_reset(struct ln8000_info *info);
|
||||
void ln8000_update_opmode(struct ln8000_info *info);
|
||||
int ln8000_change_opmode(struct ln8000_info *info, unsigned int target_mode);
|
||||
int ln8000_get_adc_data(struct ln8000_info *info, unsigned int ch, int *result);
|
||||
int psy_chg_get_charging_enabled(struct ln8000_info *info);
|
||||
int psy_chg_get_ti_alarm_status(struct ln8000_info *info);
|
||||
int psy_chg_get_it_bus_error_status(struct ln8000_info *info);
|
||||
int psy_chg_get_ti_fault_status(struct ln8000_info *info);
|
||||
int psy_chg_set_charging_enable(struct ln8000_info *info, int val);
|
||||
int psy_chg_set_present(struct ln8000_info *info, int val);
|
||||
|
||||
#endif /* __LN8000_H__ */
|
118
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000_iio.h
vendored
Normal file
118
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000_iio.h
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LN8000_IIO_H
|
||||
#define __LN8000_IIO_H
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/qti_power_supply.h>
|
||||
#include <dt-bindings/iio/qti_power_supply_iio.h>
|
||||
|
||||
#define LN8000_RCP_PATCH 1
|
||||
|
||||
struct ln8000_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define LN8000_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define LN8000_CHAN_VOLT(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define LN8000_CHAN_CUR(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define LN8000_CHAN_TEMP(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define LN8000_CHAN_POW(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_POWER, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define LN8000_CHAN_ENERGY(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_ENERGY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define LN8000_CHAN_COUNT(_name, _num) \
|
||||
LN8000_IIO_CHAN(_name, _num, IIO_COUNT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
static const struct ln8000_iio_channels ln8000_iio_psy_channels[] = {
|
||||
LN8000_CHAN_ENERGY("ln_present", PSY_IIO_SC_PRESENT) LN8000_CHAN_ENERGY(
|
||||
"ln_charging_enabled",
|
||||
PSY_IIO_SC_CHARGING_ENABLED) LN8000_CHAN_ENERGY("ln_status",
|
||||
PSY_IIO_SC_STATUS)
|
||||
LN8000_CHAN_ENERGY("ln_battery_present", PSY_IIO_SC_BATTERY_PRESENT) LN8000_CHAN_ENERGY(
|
||||
"ln_vbus_present",
|
||||
PSY_IIO_SC_VBUS_PRESENT) LN8000_CHAN_VOLT("ln_battery_voltage",
|
||||
PSY_IIO_SC_BATTERY_VOLTAGE)
|
||||
LN8000_CHAN_CUR("ln_battery_current", PSY_IIO_SC_BATTERY_CURRENT) LN8000_CHAN_TEMP(
|
||||
"ln_battery_temperature",
|
||||
PSY_IIO_SC_BATTERY_TEMPERATURE) LN8000_CHAN_VOLT("ln_bus_voltage",
|
||||
PSY_IIO_SC_BUS_VOLTAGE)
|
||||
LN8000_CHAN_CUR("ln_bus_current", PSY_IIO_SC_BUS_CURRENT) LN8000_CHAN_TEMP(
|
||||
"ln_bus_temperature",
|
||||
PSY_IIO_SC_BUS_TEMPERATURE)
|
||||
LN8000_CHAN_TEMP(
|
||||
"ln_die_temperature",
|
||||
PSY_IIO_SC_DIE_TEMPERATURE)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_alarm_status",
|
||||
PSY_IIO_SC_ALARM_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_fault_status",
|
||||
PSY_IIO_SC_FAULT_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_vbus_error_status",
|
||||
PSY_IIO_SC_VBUS_ERROR_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_reg_status",
|
||||
PSY_IIO_SC_REG_STATUS)
|
||||
};
|
||||
|
||||
static const struct ln8000_iio_channels ln8000_slave_iio_psy_channels[] = {
|
||||
LN8000_CHAN_ENERGY("ln_present_slave", PSY_IIO_SC_PRESENT) LN8000_CHAN_ENERGY(
|
||||
"ln_charging_enabled_slave",
|
||||
PSY_IIO_SC_CHARGING_ENABLED) LN8000_CHAN_ENERGY("ln_status_slave",
|
||||
PSY_IIO_SC_STATUS)
|
||||
LN8000_CHAN_ENERGY("ln_battery_present_slave", PSY_IIO_SC_BATTERY_PRESENT) LN8000_CHAN_ENERGY(
|
||||
"ln_vbus_present_slave",
|
||||
PSY_IIO_SC_VBUS_PRESENT) LN8000_CHAN_VOLT("ln_battery_voltage_slave",
|
||||
PSY_IIO_SC_BATTERY_VOLTAGE)
|
||||
LN8000_CHAN_CUR("ln_battery_current_slave", PSY_IIO_SC_BATTERY_CURRENT) LN8000_CHAN_TEMP(
|
||||
"ln_battery_temperature_slave",
|
||||
PSY_IIO_SC_BATTERY_TEMPERATURE) LN8000_CHAN_VOLT("ln_bus_voltage_slave",
|
||||
PSY_IIO_SC_BUS_VOLTAGE)
|
||||
LN8000_CHAN_CUR("ln_bus_current_slave", PSY_IIO_SC_BUS_CURRENT) LN8000_CHAN_TEMP(
|
||||
"ln_bus_temperature_slave",
|
||||
PSY_IIO_SC_BUS_TEMPERATURE)
|
||||
LN8000_CHAN_TEMP(
|
||||
"ln_die_temperature_slave",
|
||||
PSY_IIO_SC_DIE_TEMPERATURE)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_alarm_status_slave",
|
||||
PSY_IIO_SC_ALARM_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_fault_status_slave",
|
||||
PSY_IIO_SC_FAULT_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_vbus_error_status_slave",
|
||||
PSY_IIO_SC_VBUS_ERROR_STATUS)
|
||||
LN8000_CHAN_ENERGY(
|
||||
"ln_reg_status_slave",
|
||||
PSY_IIO_SC_REG_STATUS)
|
||||
};
|
||||
|
||||
int ln_init_iio_psy(struct ln8000_info *chip);
|
||||
|
||||
#endif /* __LN8000_IIO_H */
|
292
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000_reg.h
vendored
Normal file
292
drivers/power/supply/xiaomi/platform/external/cp/inc/ln8000_reg.h
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
|
||||
/*
|
||||
* ln8000-charger.h - Charger driver for LIONSEMI LN8000
|
||||
*
|
||||
* Copyright (C) 2021 Lion Semiconductor Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LN8000_CHARGER_H__
|
||||
#define __LN8000_CHARGER_H__
|
||||
|
||||
/* For support TI extend propertis */
|
||||
#define BAT_OVP_FAULT_SHIFT 0
|
||||
#define BAT_OCP_FAULT_SHIFT 1
|
||||
#define BUS_OVP_FAULT_SHIFT 2
|
||||
#define BUS_OCP_FAULT_SHIFT 3
|
||||
#define BAT_THERM_FAULT_SHIFT 4
|
||||
#define BUS_THERM_FAULT_SHIFT 5
|
||||
#define DIE_THERM_FAULT_SHIFT 6
|
||||
|
||||
#define BAT_OVP_ALARM_SHIFT 0
|
||||
#define BAT_OCP_ALARM_SHIFT 1
|
||||
#define BUS_OVP_ALARM_SHIFT 2
|
||||
#define BUS_OCP_ALARM_SHIFT 3
|
||||
#define BAT_THERM_ALARM_SHIFT 4
|
||||
#define BUS_THERM_ALARM_SHIFT 5
|
||||
#define DIE_THERM_ALARM_SHIFT 6
|
||||
#define BAT_UCP_ALARM_SHIFT 7
|
||||
|
||||
#define VBAT_REG_STATUS_SHIFT 0
|
||||
#define IBAT_REG_STATUS_SHIFT 1
|
||||
|
||||
#if 0
|
||||
enum hvdcp3_type {
|
||||
HVDCP3_NONE = 0,
|
||||
HVDCP3_CLASSA_18W,
|
||||
HVDCP3_CLASSB_27W,
|
||||
HVDCP3P5_CLASSA_18W,
|
||||
HVDCP3P5_CLASSB_27W,
|
||||
};
|
||||
#else
|
||||
enum hvdcp3_type {
|
||||
HVDCP3_NONE = 0,
|
||||
HVDCP3_CLASSA_18W,
|
||||
HVDCP3_CLASSB_27W,
|
||||
HVDCP3_P_CLASSA_18W,
|
||||
HVDCP3_P_CLASSB_27W,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* bus protection values for QC */
|
||||
#define BUS_OVP_FOR_QC \
|
||||
13000000 /* ln8000 didn't used 10V, (support tot 6.5V, 11V, 12V, 13V) */
|
||||
#define BUS_OVP_ALARM_FOR_QC 9500000
|
||||
#define BUS_OCP_FOR_QC_CLASS_A 3250000
|
||||
#define BUS_OCP_ALARM_FOR_QC_CLASS_A 2000000
|
||||
#define BUS_OCP_FOR_QC_CLASS_B 4000000
|
||||
#define BUS_OCP_ALARM_FOR_QC_CLASS_B 3000000
|
||||
|
||||
#define BUS_OVP_FOR_QC35 13000000
|
||||
#define BUS_OVP_ALARM_FOR_QC35 9500000
|
||||
#define BUS_OCP_FOR_QC35_CLASS_A_P 3000000
|
||||
#define BUS_OCP_ALARM_FOR_QC35_CLASS_A_P 2550000
|
||||
|
||||
/**
|
||||
* ln8000 device descripion definition
|
||||
*/
|
||||
#define ASSIGNED_BITS(_end, _start) ((BIT(_end) - BIT(_start)) + BIT(_end))
|
||||
|
||||
/* register map description */
|
||||
enum ln8000_int1_desc {
|
||||
LN8000_MASK_FAULT_INT = BIT(7),
|
||||
LN8000_MASK_NTC_PROT_INT = BIT(6),
|
||||
LN8000_MASK_CHARGE_PHASE_INT = BIT(5),
|
||||
LN8000_MASK_MODE_INT = BIT(4),
|
||||
LN8000_MASK_REV_CURR_INT = BIT(3),
|
||||
LN8000_MASK_TEMP_INT = BIT(2),
|
||||
LN8000_MASK_ADC_DONE_INT = BIT(1),
|
||||
LN8000_MASK_TIMER_INT = BIT(0),
|
||||
};
|
||||
|
||||
enum ln8000_sys_sts_desc {
|
||||
LN8000_MASK_IIN_LOOP_STS = BIT(7),
|
||||
LN8000_MASK_VFLOAT_LOOP_STS = BIT(6),
|
||||
LN8000_MASK_BYPASS_ENABLED = BIT(3),
|
||||
LN8000_MASK_SWITCHING_ENABLED = BIT(2),
|
||||
LN8000_MASK_STANDBY_STS = BIT(1),
|
||||
LN8000_MASK_SHUTDOWN_STS = BIT(0),
|
||||
};
|
||||
|
||||
enum ln8000_safety_sts_desc {
|
||||
LN8000_MASK_TEMP_MAX_STS = BIT(6),
|
||||
LN8000_MASK_TEMP_REGULATION_STS = BIT(5),
|
||||
LN8000_MASK_NTC_ALARM_STS = BIT(4),
|
||||
LN8000_MASK_NTC_SHUTDOWN_STS = BIT(3),
|
||||
LN8000_MASK_REV_IIN_STS = BIT(2),
|
||||
};
|
||||
|
||||
enum ln8000_fault1_sts_desc {
|
||||
LN8000_MASK_WATCHDOG_TIMER_STS = BIT(7),
|
||||
LN8000_MASK_VBAT_OV_STS = BIT(6),
|
||||
LN8000_MASK_VAC_UNPLUG_STS = BIT(4),
|
||||
LN8000_MASK_VAC_OV_STS = BIT(3),
|
||||
LN8000_MASK_VIN_OV_STS = BIT(1),
|
||||
LN8000_MASK_VFAULTS = ASSIGNED_BITS(6, 0),
|
||||
};
|
||||
|
||||
enum ln8000_fault2_sts_desc {
|
||||
LN8000_MASK_IIN_OC_DETECTED = BIT(7),
|
||||
};
|
||||
|
||||
enum ln8000_ldo_sts_desc {
|
||||
LN8000_MASK_VBAT_MIN_OK_STS = BIT(7),
|
||||
LN8000_MASK_CHARGE_TERM_STS = BIT(5),
|
||||
LN8000_MASK_RECHARGE_STS = BIT(4),
|
||||
};
|
||||
|
||||
enum ln8000_regulation_ctrl_desc {
|
||||
LN8000_BIT_ENABLE_VFLOAT_LOOP_INT = 7,
|
||||
LN8000_BIT_ENABLE_IIN_LOOP_INT = 6,
|
||||
LN8000_BIT_DISABLE_VFLOAT_LOOP = 5,
|
||||
LN8000_BIT_DISABLE_IIN_LOOP = 4,
|
||||
LN8000_BIT_TEMP_MAX_EN = 2,
|
||||
LN8000_BIT_TEMP_REG_EN = 1,
|
||||
};
|
||||
|
||||
enum ln8000_sys_ctrl_desc {
|
||||
LN8000_BIT_STANDBY_EN = 3,
|
||||
LN8000_BIT_REV_IIN_DET = 2,
|
||||
LN8000_BIT_SOFT_START_EN = 1,
|
||||
LN8000_BIT_EN_1TO1 = 0,
|
||||
};
|
||||
|
||||
enum ln8000_fault_ctrl_desc {
|
||||
LN8000_BIT_DISABLE_IIN_OCP = 6,
|
||||
LN8000_BIT_DISABLE_VBAT_OV = 5,
|
||||
LN8000_BIT_DISABLE_VAC_OV = 4,
|
||||
LN8000_BIT_DISABLE_VAC_UV = 3,
|
||||
LN8000_BIT_DISABLE_VIN_OV = 2,
|
||||
};
|
||||
|
||||
enum ln8000_bc_op1_desc {
|
||||
LN8000_BIT_DUAL_FUNCTION_EN = 2,
|
||||
LN8000_BIT_DUAL_CFG = 1,
|
||||
LN8000_BIT_DUAL_LOCKOUT_EN = 0,
|
||||
};
|
||||
|
||||
enum ln8000_reg_addr {
|
||||
LN8000_REG_DEVICE_ID = 0x00,
|
||||
LN8000_REG_INT1 = 0x01,
|
||||
LN8000_REG_INT1_MSK = 0x02,
|
||||
LN8000_REG_SYS_STS = 0x03,
|
||||
LN8000_REG_SAFETY_STS = 0x04,
|
||||
LN8000_REG_FAULT1_STS = 0x05,
|
||||
LN8000_REG_FAULT2_STS = 0x06,
|
||||
LN8000_REG_CURR1_STS = 0x07,
|
||||
LN8000_REG_LDO_STS = 0x08,
|
||||
LN8000_REG_ADC01_STS = 0x09,
|
||||
LN8000_REG_ADC02_STS = 0x0A,
|
||||
LN8000_REG_ADC03_STS = 0x0B,
|
||||
LN8000_REG_ADC04_STS = 0x0C,
|
||||
LN8000_REG_ADC05_STS = 0x0D,
|
||||
LN8000_REG_ADC06_STS = 0x0E,
|
||||
LN8000_REG_ADC07_STS = 0x0F,
|
||||
LN8000_REG_ADC08_STS = 0x10,
|
||||
LN8000_REG_ADC09_STS = 0x11,
|
||||
LN8000_REG_ADC10_STS = 0x12,
|
||||
LN8000_REG_IIN_CTRL = 0x1B,
|
||||
LN8000_REG_REGULATION_CTRL = 0x1C,
|
||||
LN8000_REG_PWR_CTRL = 0x1D,
|
||||
LN8000_REG_SYS_CTRL = 0x1E,
|
||||
LN8000_REG_LDO_CTRL = 0x1F,
|
||||
LN8000_REG_GLITCH_CTRL = 0x20,
|
||||
LN8000_REG_FAULT_CTRL = 0x21,
|
||||
LN8000_REG_NTC_CTRL = 0x22,
|
||||
LN8000_REG_ADC_CTRL = 0x23,
|
||||
LN8000_REG_ADC_CFG = 0x24,
|
||||
LN8000_REG_RECOVERY_CTRL = 0x25,
|
||||
LN8000_REG_TIMER_CTRL = 0x26,
|
||||
LN8000_REG_THRESHOLD_CTRL = 0x27,
|
||||
LN8000_REG_V_FLOAT_CTRL = 0x28,
|
||||
LN8000_REG_CHARGE_CTRL = 0x29,
|
||||
LN8000_REG_LION_CTRL = 0x30,
|
||||
LN8000_REG_BC_OP_1 = 0x41,
|
||||
LN8000_REG_BC_OP_2 = 0x42,
|
||||
LN8000_REG_BC_STS_A = 0x49,
|
||||
LN8000_REG_BC_STS_B = 0x4A,
|
||||
LN8000_REG_BC_STS_C = 0x4B,
|
||||
LN8000_REG_BC_STS_D = 0x4C,
|
||||
LN8000_REG_BC_STS_E = 0x4D,
|
||||
LN8000_REG_MAX,
|
||||
};
|
||||
|
||||
/* device feature configuration desc */
|
||||
#define LN8000_DEVICE_ID 0x42
|
||||
|
||||
enum ln8000_role {
|
||||
LN_ROLE_STANDALONE = 0x0,
|
||||
LN_ROLE_MASTER = 0x1,
|
||||
LN_ROLE_SLAVE = 0x2,
|
||||
};
|
||||
|
||||
enum ln8000_opmode_ {
|
||||
LN8000_OPMODE_UNKNOWN = 0x0,
|
||||
LN8000_OPMODE_STANDBY = 0x1,
|
||||
LN8000_OPMODE_BYPASS = 0x2,
|
||||
LN8000_OPMODE_SWITCHING = 0x3,
|
||||
};
|
||||
|
||||
enum ln8000_vac_ov_cfg_desc {
|
||||
LN8000_VAC_OVP_6P5V = 0x0,
|
||||
LN8000_VAC_OVP_11V = 0x1,
|
||||
LN8000_VAC_OVP_12V = 0x2,
|
||||
LN8000_VAC_OVP_13V = 0x3,
|
||||
};
|
||||
|
||||
enum ln8000_watchdpg_cfg_desc {
|
||||
LN8000_WATCHDOG_5SEC = 0x0,
|
||||
LN8000_WATCHDOG_10SEC = 0x1,
|
||||
LN8000_WATCHDOG_20SEC = 0x2,
|
||||
LN8000_WATCHDOG_40SEC = 0x3,
|
||||
LN8000_WATCHDOG_MAX
|
||||
};
|
||||
|
||||
enum ln8000_adc_channel_index {
|
||||
LN8000_ADC_CH_VOUT = 1,
|
||||
LN8000_ADC_CH_VIN,
|
||||
LN8000_ADC_CH_VBAT,
|
||||
LN8000_ADC_CH_VAC,
|
||||
LN8000_ADC_CH_IIN,
|
||||
LN8000_ADC_CH_DIETEMP,
|
||||
LN8000_ADC_CH_TSBAT,
|
||||
LN8000_ADC_CH_TSBUS,
|
||||
LN8000_ADC_CH_ALL
|
||||
};
|
||||
|
||||
enum ln8000_adc_mode_desc { /* used FORCE_ADC_MODE + ADC_SHUTDOWN_CFG */
|
||||
ADC_AUTO_HIB_MODE = 0x0,
|
||||
ADC_AUTO_SHD_MODE = 0x1,
|
||||
ADC_SHUTDOWN_MODE = 0x2,
|
||||
ADC_HIBERNATE_MODE = 0x4,
|
||||
ADC_NORMAL_MODE = 0x6,
|
||||
};
|
||||
|
||||
enum ln8000_adc_hibernate_delay_desc {
|
||||
ADC_HIBERNATE_500MS = 0x0,
|
||||
ADC_HIBERNATE_1S = 0x1,
|
||||
ADC_HIBERNATE_2S = 0x2,
|
||||
ADC_HIBERNATE_4S = 0x3,
|
||||
};
|
||||
|
||||
/* electrical numeric calculation unit description */
|
||||
#define LN8000_VBAT_FLOAT_MIN 3725000 /* unit = uV */
|
||||
#define LN8000_VBAT_FLOAT_MAX 5000000
|
||||
#define LN8000_VBAT_FLOAT_LSB 5000
|
||||
#define LN8000_ADC_VOUT_STEP 5000 /* 5mV= 5000uV LSB (0V ~ 5.115V) */
|
||||
#define LN8000_ADC_VIN_STEP 16000 /* 16mV=16000uV LSB (0V ~ 16.386V) */
|
||||
#define LN8000_ADC_VBAT_STEP 5000 /* 5mV= 5000uV LSB (0V ~ 5.115V) */
|
||||
#define LN8000_ADC_VBAT_MIN 1000000 /* 1V */
|
||||
#define LN8000_ADC_VAC_STEP 16000 /* 16mV=16000uV LSB (0V ~ 16.386V) */
|
||||
#define LN8000_ADC_VAC_OS 5
|
||||
#define LN8000_ADC_IIN_STEP 4890 /* 4.89mA=4890uA LSB (0A ~ 5A) */
|
||||
#define LN8000_ADC_DIETEMP_STEP \
|
||||
4350 /* 0.435C LSB = 4350dC/1000 (-25C ~ 160C) */
|
||||
#define LN8000_ADC_DIETEMP_DENOM 1000 /* 1000 */
|
||||
#define LN8000_ADC_DIETEMP_MIN (-250) /* -25C = -250dC */
|
||||
#define LN8000_ADC_DIETEMP_MAX 1600 /* 160C = 1600dC */
|
||||
#define LN8000_ADC_NTCV_STEP 2933 /* 2.933mV=2933uV LSB (0V ~ 3V) */
|
||||
#define LN8000_IIN_CFG_MIN 500000 /* 500mA=500,000uA */
|
||||
#define LN8000_IIN_CFG_LSB 50000 /* 50mA=50,000uA */
|
||||
|
||||
/* device default values */
|
||||
#define LN8000_BAT_OVP_DEFAULT 4440000
|
||||
#define LN8000_BUS_OVP_DEFAULT 12800000
|
||||
#define LN8000_BUS_OCP_DEFAULT 2000000
|
||||
|
||||
#define LN8000_NTC_ALARM_CFG_DEFAULT 226 /* NTC alarm threshold (~40C) */
|
||||
#define LN8000_NTC_SHUTDOWN_CFG 2 /* NTC shutdown config (-16LSB ~ 4.3C) */
|
||||
#define LN8000_DEFAULT_FSW_CFG 8 /* 8=440kHz, switching freq */
|
||||
#define LN8000_IIN_CFG_DEFAULT 2000000 /* 2A=2,000,000uA, input current limit */
|
||||
|
||||
#endif /* __LN8000_CHARGER_H__ */
|
329
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a.h
vendored
Normal file
329
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a.h
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
|
||||
#ifndef __SC8551A_H__
|
||||
#define __SC8551A_H__
|
||||
|
||||
#define pr_fmt(fmt) "[sc8551] %s: " fmt, __func__
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
|
||||
typedef enum {
|
||||
ADC_IBUS,
|
||||
ADC_VBUS,
|
||||
ADC_VAC,
|
||||
ADC_VOUT,
|
||||
ADC_VBAT,
|
||||
ADC_IBAT,
|
||||
ADC_TBUS,
|
||||
ADC_TBAT,
|
||||
ADC_TDIE,
|
||||
ADC_MAX_NUM,
|
||||
} ADC_CH;
|
||||
|
||||
#define SC8551_ROLE_STDALONE 0
|
||||
#define SC8551_ROLE_SLAVE 1
|
||||
#define SC8551_ROLE_MASTER 2
|
||||
|
||||
enum {
|
||||
SC8551_STDALONE,
|
||||
SC8551_SLAVE,
|
||||
SC8551_MASTER,
|
||||
};
|
||||
|
||||
static int sc8551_mode_data[] = {
|
||||
[SC8551_STDALONE] = SC8551_ROLE_STDALONE,
|
||||
[SC8551_MASTER] = SC8551_ROLE_MASTER,
|
||||
[SC8551_SLAVE] = SC8551_ROLE_SLAVE,
|
||||
};
|
||||
|
||||
#define BAT_OVP_ALARM BIT(7)
|
||||
#define BAT_OCP_ALARM BIT(6)
|
||||
#define BUS_OVP_ALARM BIT(5)
|
||||
#define BUS_OCP_ALARM BIT(4)
|
||||
#define BAT_UCP_ALARM BIT(3)
|
||||
#define VBUS_INSERT BIT(2)
|
||||
#define VBAT_INSERT BIT(1)
|
||||
#define ADC_DONE BIT(0)
|
||||
|
||||
#define BAT_OVP_FAULT BIT(7)
|
||||
#define BAT_OCP_FAULT BIT(6)
|
||||
#define BUS_OVP_FAULT BIT(5)
|
||||
#define BUS_OCP_FAULT BIT(4)
|
||||
#define TBUS_TBAT_ALARM BIT(3)
|
||||
#define TS_BAT_FAULT BIT(2)
|
||||
#define TS_BUS_FAULT BIT(1)
|
||||
#define TS_DIE_FAULT BIT(0)
|
||||
|
||||
/*below used for comm with other module*/
|
||||
#define BAT_OVP_FAULT_SHIFT 0
|
||||
#define BAT_OCP_FAULT_SHIFT 1
|
||||
#define BUS_OVP_FAULT_SHIFT 2
|
||||
#define BUS_OCP_FAULT_SHIFT 3
|
||||
#define BAT_THERM_FAULT_SHIFT 4
|
||||
#define BUS_THERM_FAULT_SHIFT 5
|
||||
#define DIE_THERM_FAULT_SHIFT 6
|
||||
|
||||
#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT)
|
||||
#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT)
|
||||
#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT)
|
||||
#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT)
|
||||
#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT)
|
||||
#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT)
|
||||
#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT)
|
||||
|
||||
#define BAT_OVP_ALARM_SHIFT 0
|
||||
#define BAT_OCP_ALARM_SHIFT 1
|
||||
#define BUS_OVP_ALARM_SHIFT 2
|
||||
#define BUS_OCP_ALARM_SHIFT 3
|
||||
#define BAT_THERM_ALARM_SHIFT 4
|
||||
#define BUS_THERM_ALARM_SHIFT 5
|
||||
#define DIE_THERM_ALARM_SHIFT 6
|
||||
#define BAT_UCP_ALARM_SHIFT 7
|
||||
|
||||
#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT)
|
||||
#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT)
|
||||
#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT)
|
||||
#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT)
|
||||
#define BAT_THERM_ALARM_MASK (1 << BAT_THERM_ALARM_SHIFT)
|
||||
#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT)
|
||||
#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT)
|
||||
#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT)
|
||||
|
||||
#define VBAT_REG_STATUS_SHIFT 0
|
||||
#define IBAT_REG_STATUS_SHIFT 1
|
||||
|
||||
#define VBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT)
|
||||
#define IBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT)
|
||||
|
||||
#define ADC_REG_BASE SC8551_REG_16
|
||||
|
||||
#define sc_err(fmt, ...) \
|
||||
do { \
|
||||
if (sc->mode == SC8551_ROLE_MASTER) \
|
||||
printk(KERN_ERR "[sc8551-MASTER]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else if (sc->mode == SC8551_ROLE_SLAVE) \
|
||||
printk(KERN_ERR "[sc8551-SLAVE]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_ERR "[sc8551-STANDALONE]:%s:" fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define sc_info(fmt, ...) \
|
||||
do { \
|
||||
if (sc->mode == SC8551_ROLE_MASTER) \
|
||||
printk(KERN_INFO "[sc8551-MASTER]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else if (sc->mode == SC8551_ROLE_SLAVE) \
|
||||
printk(KERN_INFO "[sc8551-SLAVE]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_INFO "[sc8551-STANDALONE]:%s:" fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define sc_dbg(fmt, ...) \
|
||||
do { \
|
||||
if (sc->mode == SC8551_ROLE_MASTER) \
|
||||
printk(KERN_DEBUG "[sc8551-MASTER]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else if (sc->mode == SC8551_ROLE_SLAVE) \
|
||||
printk(KERN_DEBUG "[sc8551-SLAVE]:%s:" fmt, __func__, \
|
||||
##__VA_ARGS__); \
|
||||
else \
|
||||
printk(KERN_DEBUG "[sc8551-STANDALONE]:%s:" fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
struct sc8551_cfg {
|
||||
bool bat_ovp_disable;
|
||||
bool bat_ocp_disable;
|
||||
bool bat_ovp_alm_disable;
|
||||
bool bat_ocp_alm_disable;
|
||||
|
||||
int bat_ovp_th;
|
||||
int bat_ovp_alm_th;
|
||||
int bat_ocp_th;
|
||||
int bat_ocp_alm_th;
|
||||
|
||||
bool bus_ovp_alm_disable;
|
||||
bool bus_ocp_disable;
|
||||
bool bus_ocp_alm_disable;
|
||||
|
||||
int bus_ovp_th;
|
||||
int bus_ovp_alm_th;
|
||||
int bus_ocp_th;
|
||||
int bus_ocp_alm_th;
|
||||
|
||||
bool bat_ucp_alm_disable;
|
||||
|
||||
int bat_ucp_alm_th;
|
||||
int ac_ovp_th;
|
||||
|
||||
bool bat_therm_disable;
|
||||
bool bus_therm_disable;
|
||||
bool die_therm_disable;
|
||||
|
||||
int bat_therm_th; /*in %*/
|
||||
int bus_therm_th; /*in %*/
|
||||
int die_therm_th; /*in degC*/
|
||||
|
||||
int sense_r_mohm;
|
||||
};
|
||||
|
||||
struct sc8551 {
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
|
||||
int part_no;
|
||||
int revision;
|
||||
|
||||
int mode;
|
||||
|
||||
struct mutex data_lock;
|
||||
struct mutex i2c_rw_lock;
|
||||
struct mutex charging_disable_lock;
|
||||
struct mutex irq_complete;
|
||||
|
||||
bool irq_waiting;
|
||||
bool irq_disabled;
|
||||
bool resume_completed;
|
||||
|
||||
bool batt_present;
|
||||
bool vbus_present;
|
||||
|
||||
bool usb_present;
|
||||
bool charge_enabled; /* Register bit status */
|
||||
|
||||
bool is_sc8551;
|
||||
int vbus_error;
|
||||
|
||||
/* ADC reading */
|
||||
int vbat_volt;
|
||||
int vbus_volt;
|
||||
int vout_volt;
|
||||
int vac_volt;
|
||||
|
||||
int ibat_curr;
|
||||
int ibus_curr;
|
||||
|
||||
int bat_temp;
|
||||
int bus_temp;
|
||||
int die_temp;
|
||||
|
||||
/* alarm/fault status */
|
||||
bool bat_ovp_fault;
|
||||
bool bat_ocp_fault;
|
||||
bool bus_ovp_fault;
|
||||
bool bus_ocp_fault;
|
||||
|
||||
bool bat_ovp_alarm;
|
||||
bool bat_ocp_alarm;
|
||||
bool bus_ovp_alarm;
|
||||
bool bus_ocp_alarm;
|
||||
|
||||
bool bat_ucp_alarm;
|
||||
|
||||
bool bat_therm_alarm;
|
||||
bool bus_therm_alarm;
|
||||
bool die_therm_alarm;
|
||||
|
||||
bool bat_therm_fault;
|
||||
bool bus_therm_fault;
|
||||
bool die_therm_fault;
|
||||
|
||||
bool therm_shutdown_flag;
|
||||
bool therm_shutdown_stat;
|
||||
|
||||
bool vbat_reg;
|
||||
bool ibat_reg;
|
||||
|
||||
int prev_alarm;
|
||||
int prev_fault;
|
||||
|
||||
int chg_ma;
|
||||
int chg_mv;
|
||||
int adc_status;
|
||||
int charge_state;
|
||||
|
||||
struct sc8551_cfg *cfg;
|
||||
|
||||
int skip_writes;
|
||||
int skip_reads;
|
||||
|
||||
struct delayed_work monitor_work;
|
||||
struct dentry *debug_root;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct iio_channel *int_iio_chans;
|
||||
};
|
||||
|
||||
int sc8551_read_byte(struct sc8551 *sc, u8 reg, u8 *data);
|
||||
int sc8551_enable_charge(struct sc8551 *sc, bool enable);
|
||||
int sc8551_check_charge_enabled(struct sc8551 *sc, bool *enabled);
|
||||
int sc8551_enable_wdt(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_reg_reset(struct sc8551 *sc);
|
||||
int sc8551_enable_batovp(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_batovp_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_batovp_alarm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_batovp_alarm_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_batocp(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_batocp_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_batocp_alarm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_batocp_alarm_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_set_busovp_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_busovp_alarm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_busovp_alarm_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_busocp(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_busocp_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_busocp_alarm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_busocp_alarm_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_enable_batucp_alarm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_batucp_alarm_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_set_acovp_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_set_vdrop_th(struct sc8551 *sc, int threshold);
|
||||
int sc8551_set_vdrop_deglitch(struct sc8551 *sc, int us);
|
||||
int sc8551_enable_bat_therm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_bat_therm_th(struct sc8551 *sc, u8 threshold);
|
||||
int sc8551_enable_bus_therm(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_bus_therm_th(struct sc8551 *sc, u8 threshold);
|
||||
int sc8551_set_die_therm_th(struct sc8551 *sc, u8 threshold);
|
||||
int sc8551_enable_adc(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_adc_scanrate(struct sc8551 *sc, bool oneshot);
|
||||
int sc8551_get_adc_data(struct sc8551 *sc, int channel, int *result);
|
||||
int sc8551_set_adc_scan(struct sc8551 *sc, int channel, bool enable);
|
||||
int sc8551_set_alarm_int_mask(struct sc8551 *sc, u8 mask);
|
||||
int sc8551_set_sense_resistor(struct sc8551 *sc, int r_mohm);
|
||||
int sc8551_enable_regulation(struct sc8551 *sc, bool enable);
|
||||
int sc8551_set_ss_timeout(struct sc8551 *sc, int timeout);
|
||||
int sc8551_set_ibat_reg_th(struct sc8551 *sc, int th_ma);
|
||||
int sc8551_set_vbat_reg_th(struct sc8551 *sc, int th_mv);
|
||||
int sc8551_check_vbus_error_status(struct sc8551 *sc);
|
||||
int sc8551_detect_device(struct sc8551 *sc);
|
||||
void sc8551_check_alarm_status(struct sc8551 *sc);
|
||||
void sc8551_check_fault_status(struct sc8551 *sc);
|
||||
int sc8551_set_present(struct sc8551 *sc, bool present);
|
||||
|
||||
#endif /* __SC8551A_H__ */
|
115
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a_iio.h
vendored
Normal file
115
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a_iio.h
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SC8551A_IIO_H
|
||||
#define __SC8551A_IIO_H
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/qti_power_supply.h>
|
||||
#include <dt-bindings/iio/qti_power_supply_iio.h>
|
||||
|
||||
struct sc8551_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define SC8551_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define SC8551_CHAN_VOLT(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define SC8551_CHAN_CUR(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define SC8551_CHAN_TEMP(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define SC8551_CHAN_POW(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_POWER, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define SC8551_CHAN_ENERGY(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_ENERGY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define SC8551_CHAN_COUNT(_name, _num) \
|
||||
SC8551_IIO_CHAN(_name, _num, IIO_COUNT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
static const struct sc8551_iio_channels sc8551_iio_psy_channels[] = {
|
||||
SC8551_CHAN_ENERGY("sc_present", PSY_IIO_SC_PRESENT) SC8551_CHAN_ENERGY(
|
||||
"sc_charging_enabled",
|
||||
PSY_IIO_SC_CHARGING_ENABLED) SC8551_CHAN_ENERGY("sc_status",
|
||||
PSY_IIO_SC_STATUS)
|
||||
SC8551_CHAN_ENERGY("sc_battery_present", PSY_IIO_SC_BATTERY_PRESENT) SC8551_CHAN_ENERGY(
|
||||
"sc_vbus_present",
|
||||
PSY_IIO_SC_VBUS_PRESENT) SC8551_CHAN_VOLT("sc_battery_voltage",
|
||||
PSY_IIO_SC_BATTERY_VOLTAGE)
|
||||
SC8551_CHAN_CUR("sc_battery_current", PSY_IIO_SC_BATTERY_CURRENT) SC8551_CHAN_TEMP(
|
||||
"sc_battery_temperature",
|
||||
PSY_IIO_SC_BATTERY_TEMPERATURE) SC8551_CHAN_VOLT("sc_bus_voltage",
|
||||
PSY_IIO_SC_BUS_VOLTAGE)
|
||||
SC8551_CHAN_CUR("sc_bus_current", PSY_IIO_SC_BUS_CURRENT) SC8551_CHAN_TEMP(
|
||||
"sc_bus_temperature",
|
||||
PSY_IIO_SC_BUS_TEMPERATURE)
|
||||
SC8551_CHAN_TEMP(
|
||||
"sc_die_temperature",
|
||||
PSY_IIO_SC_DIE_TEMPERATURE)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_alarm_status",
|
||||
PSY_IIO_SC_ALARM_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_fault_status",
|
||||
PSY_IIO_SC_FAULT_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_vbus_error_status",
|
||||
PSY_IIO_SC_VBUS_ERROR_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_enable_adc",
|
||||
PSY_IIO_SC_ENABLE_ADC)
|
||||
};
|
||||
|
||||
static const struct sc8551_iio_channels sc8551_slave_iio_psy_channels[] = {
|
||||
SC8551_CHAN_ENERGY("sc_present_slave", PSY_IIO_SC_PRESENT) SC8551_CHAN_ENERGY(
|
||||
"sc_charging_enabled_slave",
|
||||
PSY_IIO_SC_CHARGING_ENABLED) SC8551_CHAN_ENERGY("sc_status_slave",
|
||||
PSY_IIO_SC_STATUS)
|
||||
SC8551_CHAN_ENERGY("sc_battery_present_slave", PSY_IIO_SC_BATTERY_PRESENT) SC8551_CHAN_ENERGY(
|
||||
"sc_vbus_present_slave",
|
||||
PSY_IIO_SC_VBUS_PRESENT) SC8551_CHAN_VOLT("sc_battery_voltage_slave",
|
||||
PSY_IIO_SC_BATTERY_VOLTAGE)
|
||||
SC8551_CHAN_CUR("sc_battery_current_slave", PSY_IIO_SC_BATTERY_CURRENT) SC8551_CHAN_TEMP(
|
||||
"sc_battery_temperature_slave",
|
||||
PSY_IIO_SC_BATTERY_TEMPERATURE) SC8551_CHAN_VOLT("sc_bus_voltage_slave",
|
||||
PSY_IIO_SC_BUS_VOLTAGE)
|
||||
SC8551_CHAN_CUR("sc_bus_current_slave", PSY_IIO_SC_BUS_CURRENT) SC8551_CHAN_TEMP(
|
||||
"sc_bus_temperature_slave",
|
||||
PSY_IIO_SC_BUS_TEMPERATURE)
|
||||
SC8551_CHAN_TEMP(
|
||||
"sc_die_temperature_slave",
|
||||
PSY_IIO_SC_DIE_TEMPERATURE)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_alarm_status_slave",
|
||||
PSY_IIO_SC_ALARM_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_fault_status_slave",
|
||||
PSY_IIO_SC_FAULT_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_vbus_error_status_slave",
|
||||
PSY_IIO_SC_VBUS_ERROR_STATUS)
|
||||
SC8551_CHAN_ENERGY(
|
||||
"sc_enable_adc_slave",
|
||||
PSY_IIO_SC_ENABLE_ADC)
|
||||
};
|
||||
|
||||
int sc_init_iio_psy(struct sc8551 *chip);
|
||||
|
||||
#endif /* __SC8551A_IIO_H */
|
702
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a_reg.h
vendored
Normal file
702
drivers/power/supply/xiaomi/platform/external/cp/inc/sc8551a_reg.h
vendored
Normal file
@ -0,0 +1,702 @@
|
||||
|
||||
#ifndef __SC8551_HEADER__
|
||||
#define __SC8551_HEADER__
|
||||
|
||||
/* Register 00h */
|
||||
#define SC8551_REG_00 0x00
|
||||
#define SC8551_BAT_OVP_DIS_MASK 0x80
|
||||
#define SC8551_BAT_OVP_DIS_SHIFT 7
|
||||
#define SC8551_BAT_OVP_ENABLE 0
|
||||
#define SC8551_BAT_OVP_DISABLE 1
|
||||
|
||||
#define SC8551_BAT_OVP_MASK 0x3F
|
||||
#define SC8551_BAT_OVP_SHIFT 0
|
||||
#define SC8551_BAT_OVP_BASE 3500
|
||||
#define SC8551_BAT_OVP_LSB 25
|
||||
|
||||
/* Register 01h */
|
||||
#define SC8551_REG_01 0x01
|
||||
#define SC8551_BAT_OVP_ALM_DIS_MASK 0x80
|
||||
#define SC8551_BAT_OVP_ALM_DIS_SHIFT 7
|
||||
#define SC8551_BAT_OVP_ALM_ENABLE 0
|
||||
#define SC8551_BAT_OVP_ALM_DISABLE 1
|
||||
|
||||
#define SC8551_BAT_OVP_ALM_MASK 0x3F
|
||||
#define SC8551_BAT_OVP_ALM_SHIFT 0
|
||||
#define SC8551_BAT_OVP_ALM_BASE 3500
|
||||
#define SC8551_BAT_OVP_ALM_LSB 25
|
||||
|
||||
/* Register 02h */
|
||||
#define SC8551_REG_02 0x02
|
||||
#define SC8551_BAT_OCP_DIS_MASK 0x80
|
||||
#define SC8551_BAT_OCP_DIS_SHIFT 7
|
||||
#define SC8551_BAT_OCP_ENABLE 0
|
||||
#define SC8551_BAT_OCP_DISABLE 1
|
||||
|
||||
#define SC8551_BAT_OCP_MASK 0x7F
|
||||
#define SC8551_BAT_OCP_SHIFT 0
|
||||
#define SC8551_BAT_OCP_BASE 2000
|
||||
#define SC8551_BAT_OCP_LSB 100
|
||||
|
||||
/* Register 03h */
|
||||
#define SC8551_REG_03 0x03
|
||||
#define SC8551_BAT_OCP_ALM_DIS_MASK 0x80
|
||||
#define SC8551_BAT_OCP_ALM_DIS_SHIFT 7
|
||||
#define SC8551_BAT_OCP_ALM_ENABLE 0
|
||||
#define SC8551_BAT_OCP_ALM_DISABLE 1
|
||||
|
||||
#define SC8551_BAT_OCP_ALM_MASK 0x7F
|
||||
#define SC8551_BAT_OCP_ALM_SHIFT 0
|
||||
#define SC8551_BAT_OCP_ALM_BASE 2000
|
||||
#define SC8551_BAT_OCP_ALM_LSB 100
|
||||
|
||||
/* Register 04h */
|
||||
#define SC8551_REG_04 0x04
|
||||
#define SC8551_BAT_UCP_ALM_DIS_MASK 0x80
|
||||
#define SC8551_BAT_UCP_ALM_DIS_SHIFT 7
|
||||
#define SC8551_BAT_UCP_ALM_ENABLE 0
|
||||
#define SC8551_BAT_UCP_ALM_DISABLE 1
|
||||
|
||||
#define SC8551_BAT_UCP_ALM_MASK 0x7F
|
||||
#define SC8551_BAT_UCP_ALM_SHIFT 0
|
||||
#define SC8551_BAT_UCP_ALM_BASE 0
|
||||
#define SC8551_BAT_UCP_ALM_LSB 50
|
||||
|
||||
/* Register 05h */
|
||||
#define SC8551_REG_05 0x05
|
||||
#define SC8551_AC_OVP_STAT_MASK 0x80
|
||||
#define SC8551_AC_OVP_STAT_SHIFT 7
|
||||
|
||||
#define SC8551_AC_OVP_FLAG_MASK 0x40
|
||||
#define SC8551_AC_OVP_FLAG_SHIFT 6
|
||||
|
||||
#define SC8551_AC_OVP_MASK_MASK 0x20
|
||||
#define SC8551_AC_OVP_MASK_SHIFT 5
|
||||
|
||||
#define SC8551_VDROP_THRESHOLD_SET_MASK 0x10
|
||||
#define SC8551_VDROP_THRESHOLD_SET_SHIFT 4
|
||||
#define SC8551_VDROP_THRESHOLD_300MV 0
|
||||
#define SC8551_VDROP_THRESHOLD_400MV 1
|
||||
|
||||
#define SC8551_VDROP_DEGLITCH_SET_MASK 0x08
|
||||
#define SC8551_VDROP_DEGLITCH_SET_SHIFT 3
|
||||
#define SC8551_VDROP_DEGLITCH_8US 0
|
||||
#define SC8551_VDROP_DEGLITCH_5MS 1
|
||||
|
||||
#define SC8551_AC_OVP_MASK 0x07
|
||||
#define SC8551_AC_OVP_SHIFT 0
|
||||
#define SC8551_AC_OVP_BASE 11
|
||||
#define SC8551_AC_OVP_LSB 1
|
||||
#define SC8551_AC_OVP_6P5V 65
|
||||
|
||||
/* Register 06h */
|
||||
#define SC8551_REG_06 0x06
|
||||
#define SC8551_VBUS_PD_EN_MASK 0x80
|
||||
#define SC8551_VBUS_PD_EN_SHIFT 7
|
||||
#define SC8551_VBUS_PD_ENABLE 1
|
||||
#define SC8551_VBUS_PD_DISABLE 0
|
||||
|
||||
#define SC8551_BUS_OVP_MASK 0x7F
|
||||
#define SC8551_BUS_OVP_SHIFT 0
|
||||
#define SC8551_BUS_OVP_BASE 6000
|
||||
#define SC8551_BUS_OVP_LSB 50
|
||||
|
||||
/* Register 07h */
|
||||
#define SC8551_REG_07 0x07
|
||||
#define SC8551_BUS_OVP_ALM_DIS_MASK 0x80
|
||||
#define SC8551_BUS_OVP_ALM_DIS_SHIFT 7
|
||||
#define SC8551_BUS_OVP_ALM_ENABLE 0
|
||||
#define SC8551_BUS_OVP_ALM_DISABLE 1
|
||||
|
||||
#define SC8551_BUS_OVP_ALM_MASK 0x7F
|
||||
#define SC8551_BUS_OVP_ALM_SHIFT 0
|
||||
#define SC8551_BUS_OVP_ALM_BASE 6000
|
||||
#define SC8551_BUS_OVP_ALM_LSB 50
|
||||
|
||||
/* Register 08h */
|
||||
#define SC8551_REG_08 0x08
|
||||
#define SC8551_BUS_OCP_DIS_MASK 0x80
|
||||
#define SC8551_BUS_OCP_DIS_SHIFT 7
|
||||
#define SC8551_BUS_OCP_ENABLE 0
|
||||
#define SC8551_BUS_OCP_DISABLE 1
|
||||
|
||||
#define SC8551_IBUS_UCP_RISE_FLAG_MASK 0x40
|
||||
#define SC8551_IBUS_UCP_RISE_FLAG_SHIFT 6
|
||||
|
||||
#define SC8551_IBUS_UCP_RISE_MASK_MASK 0x20
|
||||
#define SC8551_IBUS_UCP_RISE_MASK_SHIFT 5
|
||||
#define SC8551_IBUS_UCP_RISE_MASK_ENABLE 1
|
||||
#define SC8551_IBUS_UCP_RISE_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_IBUS_UCP_FALL_FLAG_MASK 0x10
|
||||
#define SC8551_IBUS_UCP_FALL_FLAG_SHIFT 4
|
||||
|
||||
#define SC8551_BUS_OCP_MASK 0x0F
|
||||
#define SC8551_BUS_OCP_SHIFT 0
|
||||
#define SC8551_BUS_OCP_BASE 1000
|
||||
#define SC8551_BUS_OCP_LSB 250
|
||||
|
||||
/* Register 09h */
|
||||
#define SC8551_REG_09 0x09
|
||||
#define SC8551_BUS_OCP_ALM_DIS_MASK 0x80
|
||||
#define SC8551_BUS_OCP_ALM_DIS_SHIFT 7
|
||||
#define SC8551_BUS_OCP_ALM_ENABLE 0
|
||||
#define SC8551_BUS_OCP_ALM_DISABLE 1
|
||||
|
||||
#define SC8551_BUS_OCP_ALM_MASK 0x7F
|
||||
#define SC8551_BUS_OCP_ALM_SHIFT 0
|
||||
#define SC8551_BUS_OCP_ALM_BASE 0
|
||||
#define SC8551_BUS_OCP_ALM_LSB 50
|
||||
|
||||
/* Register 0Ah */
|
||||
#define SC8551_REG_0A 0x0A
|
||||
#define SC8551_TSHUT_FLAG_MASK 0x80
|
||||
#define SC8551_TSHUT_FLAG_SHIFT 7
|
||||
|
||||
#define SC8551_TSHUT_STAT_MASK 0x40
|
||||
#define SC8551_TSHUT_STAT_SHIFT 6
|
||||
|
||||
#define SC8551_VBUS_ERRORLO_STAT_MASK 0x20
|
||||
#define SC8551_VBUS_ERRORLO_STAT_SHIFT 5
|
||||
|
||||
#define SC8551_VBUS_ERRORHI_STAT_MASK 0x10
|
||||
#define SC8551_VBUS_ERRORHI_STAT_SHIFT 4
|
||||
|
||||
#define SC8551_SS_TIMEOUT_FLAG_MASK 0x08
|
||||
#define SC8551_SS_TIMEOUT_FLAG_SHIFT 3
|
||||
|
||||
#define SC8551_CONV_SWITCHING_STAT_MASK 0x04
|
||||
#define SC8551_CONV_SWITCHING_STAT_SHIFT 2
|
||||
|
||||
#define SC8551_CONV_OCP_FLAG_MASK 0x02
|
||||
#define SC8551_CONV_OCP_FLAG_SHIFT 1
|
||||
|
||||
#define SC8551_PIN_DIAG_FALL_FLAG_MASK 0x01
|
||||
#define SC8551_PIN_DIAG_FALL_FLAG_SHIFT 0
|
||||
|
||||
/* Register 0Bh */
|
||||
#define SC8551_REG_0B 0x0B
|
||||
#define SC8551_REG_RST_MASK 0x80
|
||||
#define SC8551_REG_RST_SHIFT 7
|
||||
#define SC8551_REG_RST_ENABLE 1
|
||||
#define SC8551_REG_RST_DISABLE 0
|
||||
|
||||
#define SC8551_FSW_SET_MASK 0x70
|
||||
#define SC8551_FSW_SET_SHIFT 4
|
||||
#define SC8551_FSW_SET_300KHZ 0
|
||||
#define SC8551_FSW_SET_350KHZ 1
|
||||
#define SC8551_FSW_SET_400KHZ 2
|
||||
#define SC8551_FSW_SET_450KHZ 3
|
||||
#define SC8551_FSW_SET_500KHZ 4
|
||||
#define SC8551_FSW_SET_550KHZ 5
|
||||
#define SC8551_FSW_SET_600KHZ 6
|
||||
#define SC8551_FSW_SET_750KHZ 7
|
||||
|
||||
#define SC8551_WD_TIMEOUT_FLAG_MASK 0x08
|
||||
#define SC8551_WD_TIMEOUT_SHIFT 3
|
||||
|
||||
#define SC8551_WATCHDOG_DIS_MASK 0x04
|
||||
#define SC8551_WATCHDOG_DIS_SHIFT 2
|
||||
#define SC8551_WATCHDOG_ENABLE 0
|
||||
#define SC8551_WATCHDOG_DISABLE 1
|
||||
|
||||
#define SC8551_WATCHDOG_MASK 0x03
|
||||
#define SC8551_WATCHDOG_SHIFT 0
|
||||
#define SC8551_WATCHDOG_0P5S 0
|
||||
#define SC8551_WATCHDOG_1S 1
|
||||
#define SC8551_WATCHDOG_5S 2
|
||||
#define SC8551_WATCHDOG_30S 3
|
||||
|
||||
/* Register 0Ch */
|
||||
#define SC8551_REG_0C 0x0C
|
||||
#define SC8551_CHG_EN_MASK 0x80
|
||||
#define SC8551_CHG_EN_SHIFT 7
|
||||
#define SC8551_CHG_ENABLE 1
|
||||
#define SC8551_CHG_DISABLE 0
|
||||
|
||||
#define SC8551_MS_MASK 0x60
|
||||
#define SC8551_MS_SHIFT 5
|
||||
#define SC8551_MS_STANDALONE 0
|
||||
#define SC8551_MS_SLAVE 1
|
||||
#define SC8551_MS_MASTER 2
|
||||
|
||||
#define SC8551_FREQ_SHIFT_MASK 0x18
|
||||
#define SC8551_FREQ_SHIFT_SHIFT 3
|
||||
#define SC8551_FREQ_SHIFT_NORMINAL 0
|
||||
#define SC8551_FREQ_SHIFT_POSITIVE10 1
|
||||
#define SC8551_FREQ_SHIFT_NEGATIVE10 2
|
||||
#define SC8551_FREQ_SHIFT_SPREAD_SPECTRUM 3
|
||||
|
||||
#define SC8551_TSBUS_DIS_MASK 0x04
|
||||
#define SC8551_TSBUS_DIS_SHIFT 2
|
||||
#define SC8551_TSBUS_ENABLE 0
|
||||
#define SC8551_TSBUS_DISABLE 1
|
||||
|
||||
#define SC8551_TSBAT_DIS_MASK 0x02
|
||||
#define SC8551_TSBAT_DIS_SHIFT 1
|
||||
#define SC8551_TSBAT_ENABLE 0
|
||||
#define SC8551_TSBAT_DISABLE 1
|
||||
|
||||
/* Register 0Dh */
|
||||
#define SC8551_REG_0D 0x0D
|
||||
#define SC8551_BAT_OVP_ALM_STAT_MASK 0x80
|
||||
#define SC8551_BAT_OVP_ALM_STAT_SHIFT 7
|
||||
|
||||
#define SC8551_BAT_OCP_ALM_STAT_MASK 0x40
|
||||
#define SC8551_BAT_OCP_ALM_STAT_SHIFT 6
|
||||
|
||||
#define SC8551_BUS_OVP_ALM_STAT_MASK 0x20
|
||||
#define SC8551_BUS_OVP_ALM_STAT_SHIFT 5
|
||||
|
||||
#define SC8551_BUS_OCP_ALM_STAT_MASK 0x10
|
||||
#define SC8551_BUS_OCP_ALM_STAT_SHIFT 4
|
||||
|
||||
#define SC8551_BAT_UCP_ALM_STAT_MASK 0x08
|
||||
#define SC8551_BAT_UCP_ALM_STAT_SHIFT 3
|
||||
|
||||
#define SC8551_ADAPTER_INSERT_STAT_MASK 0x04
|
||||
#define SC8551_ADAPTER_INSERT_STAT_SHIFT 2
|
||||
|
||||
#define SC8551_VBAT_INSERT_STAT_MASK 0x02
|
||||
#define SC8551_VBAT_INSERT_STAT_SHIFT 1
|
||||
|
||||
#define SC8551_ADC_DONE_STAT_MASK 0x01
|
||||
#define SC8551_ADC_DONE_STAT_SHIFT 0
|
||||
#define SC8551_ADC_DONE_STAT_COMPLETE 1
|
||||
#define SC8551_ADC_DONE_STAT_NOTCOMPLETE 0
|
||||
|
||||
/* Register 0Eh */
|
||||
#define SC8551_REG_0E 0x0E
|
||||
#define SC8551_BAT_OVP_ALM_FLAG_MASK 0x80
|
||||
#define SC8551_BAT_OVP_ALM_FLAG_SHIFT 7
|
||||
|
||||
#define SC8551_BAT_OCP_ALM_FLAG_MASK 0x40
|
||||
#define SC8551_BAT_OCP_ALM_FLAG_SHIFT 6
|
||||
|
||||
#define SC8551_BUS_OVP_ALM_FLAG_MASK 0x20
|
||||
#define SC8551_BUS_OVP_ALM_FLAG_SHIFT 5
|
||||
|
||||
#define SC8551_BUS_OCP_ALM_FLAG_MASK 0x10
|
||||
#define SC8551_BUS_OCP_ALM_FLAG_SHIFT 4
|
||||
|
||||
#define SC8551_BAT_UCP_ALM_FLAG_MASK 0x08
|
||||
#define SC8551_BAT_UCP_ALM_FLAG_SHIFT 3
|
||||
|
||||
#define SC8551_ADAPTER_INSERT_FLAG_MASK 0x04
|
||||
#define SC8551_ADAPTER_INSERT_FLAG_SHIFT 2
|
||||
|
||||
#define SC8551_VBAT_INSERT_FLAG_MASK 0x02
|
||||
#define SC8551_VBAT_INSERT_FLAG_SHIFT 1
|
||||
|
||||
#define SC8551_ADC_DONE_FLAG_MASK 0x01
|
||||
#define SC8551_ADC_DONE_FLAG_SHIFT 0
|
||||
#define SC8551_ADC_DONE_FLAG_COMPLETE 1
|
||||
#define SC8551_ADC_DONE_FLAG_NOTCOMPLETE 0
|
||||
|
||||
/* Register 0Fh */
|
||||
#define SC8551_REG_0F 0x0F
|
||||
#define SC8551_BAT_OVP_ALM_MASK_MASK 0x80
|
||||
#define SC8551_BAT_OVP_ALM_MASK_SHIFT 7
|
||||
#define SC8551_BAT_OVP_ALM_MASK_ENABLE 1
|
||||
#define SC8551_BAT_OVP_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BAT_OCP_ALM_MASK_MASK 0x40
|
||||
#define SC8551_BAT_OCP_ALM_MASK_SHIFT 6
|
||||
#define SC8551_BAT_OCP_ALM_MASK_ENABLE 1
|
||||
#define SC8551_BAT_OCP_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BUS_OVP_ALM_MASK_MASK 0x20
|
||||
#define SC8551_BUS_OVP_ALM_MASK_SHIFT 5
|
||||
#define SC8551_BUS_OVP_ALM_MASK_ENABLE 1
|
||||
#define SC8551_BUS_OVP_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BUS_OCP_ALM_MASK_MASK 0x10
|
||||
#define SC8551_BUS_OCP_ALM_MASK_SHIFT 4
|
||||
#define SC8551_BUS_OCP_ALM_MASK_ENABLE 1
|
||||
#define SC8551_BUS_OCP_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BAT_UCP_ALM_MASK_MASK 0x08
|
||||
#define SC8551_BAT_UCP_ALM_MASK_SHIFT 3
|
||||
#define SC8551_BAT_UCP_ALM_MASK_ENABLE 1
|
||||
#define SC8551_BAT_UCP_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_ADAPTER_INSERT_MASK_MASK 0x04
|
||||
#define SC8551_ADAPTER_INSERT_MASK_SHIFT 2
|
||||
#define SC8551_ADAPTER_INSERT_MASK_ENABLE 1
|
||||
#define SC8551_ADAPTER_INSERT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_VBAT_INSERT_MASK_MASK 0x02
|
||||
#define SC8551_VBAT_INSERT_MASK_SHIFT 1
|
||||
#define SC8551_VBAT_INSERT_MASK_ENABLE 1
|
||||
#define SC8551_VBAT_INSERT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_ADC_DONE_MASK_MASK 0x01
|
||||
#define SC8551_ADC_DONE_MASK_SHIFT 0
|
||||
#define SC8551_ADC_DONE_MASK_ENABLE 1
|
||||
#define SC8551_ADC_DONE_MASK_DISABLE 0
|
||||
|
||||
/* Register 10h */
|
||||
#define SC8551_REG_10 0x10
|
||||
#define SC8551_BAT_OVP_FLT_STAT_MASK 0x80
|
||||
#define SC8551_BAT_OVP_FLT_STAT_SHIFT 7
|
||||
|
||||
#define SC8551_BAT_OCP_FLT_STAT_MASK 0x40
|
||||
#define SC8551_BAT_OCP_FLT_STAT_SHIFT 6
|
||||
|
||||
#define SC8551_BUS_OVP_FLT_STAT_MASK 0x20
|
||||
#define SC8551_BUS_OVP_FLT_STAT_SHIFT 5
|
||||
|
||||
#define SC8551_BUS_OCP_FLT_STAT_MASK 0x10
|
||||
#define SC8551_BUS_OCP_FLT_STAT_SHIFT 4
|
||||
|
||||
#define SC8551_TSBUS_TSBAT_ALM_STAT_MASK 0x08
|
||||
#define SC8551_TSBUS_TSBAT_ALM_STAT_SHIFT 3
|
||||
|
||||
#define SC8551_TSBAT_FLT_STAT_MASK 0x04
|
||||
#define SC8551_TSBAT_FLT_STAT_SHIFT 2
|
||||
|
||||
#define SC8551_TSBUS_FLT_STAT_MASK 0x02
|
||||
#define SC8551_TSBUS_FLT_STAT_SHIFT 1
|
||||
|
||||
#define SC8551_TDIE_ALM_STAT_MASK 0x01
|
||||
#define SC8551_TDIE_ALM_STAT_SHIFT 0
|
||||
|
||||
/* Register 11h */
|
||||
#define SC8551_REG_11 0x11
|
||||
#define SC8551_BAT_OVP_FLT_FLAG_MASK 0x80
|
||||
#define SC8551_BAT_OVP_FLT_FLAG_SHIFT 7
|
||||
|
||||
#define SC8551_BAT_OCP_FLT_FLAG_MASK 0x40
|
||||
#define SC8551_BAT_OCP_FLT_FLAG_SHIFT 6
|
||||
|
||||
#define SC8551_BUS_OVP_FLT_FLAG_MASK 0x20
|
||||
#define SC8551_BUS_OVP_FLT_FLAG_SHIFT 5
|
||||
|
||||
#define SC8551_BUS_OCP_FLT_FLAG_MASK 0x10
|
||||
#define SC8551_BUS_OCP_FLT_FLAG_SHIFT 4
|
||||
|
||||
#define SC8551_TSBUS_TSBAT_ALM_FLAG_MASK 0x08
|
||||
#define SC8551_TSBUS_TSBAT_ALM_FLAG_SHIFT 3
|
||||
|
||||
#define SC8551_TSBAT_FLT_FLAG_MASK 0x04
|
||||
#define SC8551_TSBAT_FLT_FLAG_SHIFT 2
|
||||
|
||||
#define SC8551_TSBUS_FLT_FLAG_MASK 0x02
|
||||
#define SC8551_TSBUS_FLT_FLAG_SHIFT 1
|
||||
|
||||
#define SC8551_TDIE_ALM_FLAG_MASK 0x01
|
||||
#define SC8551_TDIE_ALM_FLAG_SHIFT 0
|
||||
|
||||
/* Register 12h */
|
||||
#define SC8551_REG_12 0x12
|
||||
#define SC8551_BAT_OVP_FLT_MASK_MASK 0x80
|
||||
#define SC8551_BAT_OVP_FLT_MASK_SHIFT 7
|
||||
#define SC8551_BAT_OVP_FLT_MASK_ENABLE 1
|
||||
#define SC8551_BAT_OVP_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BAT_OCP_FLT_MASK_MASK 0x40
|
||||
#define SC8551_BAT_OCP_FLT_MASK_SHIFT 6
|
||||
#define SC8551_BAT_OCP_FLT_MASK_ENABLE 1
|
||||
#define SC8551_BAT_OCP_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BUS_OVP_FLT_MASK_MASK 0x20
|
||||
#define SC8551_BUS_OVP_FLT_MASK_SHIFT 5
|
||||
#define SC8551_BUS_OVP_FLT_MASK_ENABLE 1
|
||||
#define SC8551_BUS_OVP_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_BUS_OCP_FLT_MASK_MASK 0x10
|
||||
#define SC8551_BUS_OCP_FLT_MASK_SHIFT 4
|
||||
#define SC8551_BUS_OCP_FLT_MASK_ENABLE 1
|
||||
#define SC8551_BUS_OCP_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_TSBUS_TSBAT_ALM_MASK_MASK 0x08
|
||||
#define SC8551_TSBUS_TSBAT_ALM_MASK_SHIFT 3
|
||||
#define SC8551_TSBUS_TSBAT_ALM_MASK_ENABLE 1
|
||||
#define SC8551_TSBUS_TSBAT_ALM_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_TSBAT_FLT_MASK_MASK 0x04
|
||||
#define SC8551_TSBAT_FLT_MASK_SHIFT 2
|
||||
#define SC8551_TSBAT_FLT_MASK_ENABLE 1
|
||||
#define SC8551_TSBAT_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_TSBUS_FLT_MASK_MASK 0x02
|
||||
#define SC8551_TSBUS_FLT_MASK_SHIFT 1
|
||||
#define SC8551_TSBUS_FLT_MASK_ENABLE 1
|
||||
#define SC8551_TSBUS_FLT_MASK_DISABLE 0
|
||||
|
||||
#define SC8551_TDIE_ALM_MASK_MASK 0x01
|
||||
#define SC8551_TDIE_ALM_MASK_SHIFT 0
|
||||
#define SC8551_TDIE_ALM_MASK_ENABLE 1
|
||||
#define SC8551_TDIE_ALM_MASK_DISABLE 0
|
||||
|
||||
/* Register 13h */
|
||||
#define SC8551_REG_13 0x13
|
||||
#define SC8551_DEV_ID_MASK 0x0F
|
||||
#define SC8551_DEV_ID_SHIFT 0
|
||||
|
||||
/* Register 14h */
|
||||
#define SC8551_REG_14 0x14
|
||||
#define SC8551_ADC_EN_MASK 0x80
|
||||
#define SC8551_ADC_EN_SHIFT 7
|
||||
#define SC8551_ADC_ENABLE 1
|
||||
#define SC8551_ADC_DISABLE 0
|
||||
|
||||
#define SC8551_ADC_RATE_MASK 0x40
|
||||
#define SC8551_ADC_RATE_SHIFT 6
|
||||
#define SC8551_ADC_RATE_CONTINOUS 0
|
||||
#define SC8551_ADC_RATE_ONESHOT 1
|
||||
|
||||
#define SC8551_IBUS_ADC_DIS_MASK 0x01
|
||||
#define SC8551_IBUS_ADC_DIS_SHIFT 0
|
||||
#define SC8551_IBUS_ADC_ENABLE 0
|
||||
#define SC8551_IBUS_ADC_DISABLE 1
|
||||
|
||||
/* Register 15h */
|
||||
#define SC8551_REG_15 0x15
|
||||
#define SC8551_VBUS_ADC_DIS_MASK 0x80
|
||||
#define SC8551_VBUS_ADC_DIS_SHIFT 7
|
||||
#define SC8551_VBUS_ADC_ENABLE 0
|
||||
#define SC8551_VBUS_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_VAC_ADC_DIS_MASK 0x40
|
||||
#define SC8551_VAC_ADC_DIS_SHIFT 6
|
||||
#define SC8551_VAC_ADC_ENABLE 0
|
||||
#define SC8551_VAC_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_VOUT_ADC_DIS_MASK 0x20
|
||||
#define SC8551_VOUT_ADC_DIS_SHIFT 5
|
||||
#define SC8551_VOUT_ADC_ENABLE 0
|
||||
#define SC8551_VOUT_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_VBAT_ADC_DIS_MASK 0x10
|
||||
#define SC8551_VBAT_ADC_DIS_SHIFT 4
|
||||
#define SC8551_VBAT_ADC_ENABLE 0
|
||||
#define SC8551_VBAT_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_IBAT_ADC_DIS_MASK 0x08
|
||||
#define SC8551_IBAT_ADC_DIS_SHIFT 3
|
||||
#define SC8551_IBAT_ADC_ENABLE 0
|
||||
#define SC8551_IBAT_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_TSBUS_ADC_DIS_MASK 0x04
|
||||
#define SC8551_TSBUS_ADC_DIS_SHIFT 2
|
||||
#define SC8551_TSBUS_ADC_ENABLE 0
|
||||
#define SC8551_TSBUS_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_TSBAT_ADC_DIS_MASK 0x02
|
||||
#define SC8551_TSBAT_ADC_DIS_SHIFT 1
|
||||
#define SC8551_TSBAT_ADC_ENABLE 0
|
||||
#define SC8551_TSBAT_ADC_DISABLE 1
|
||||
|
||||
#define SC8551_TDIE_ADC_DIS_MASK 0x01
|
||||
#define SC8551_TDIE_ADC_DIS_SHIFT 0
|
||||
#define SC8551_TDIE_ADC_ENABLE 0
|
||||
#define SC8551_TDIE_ADC_DISABLE 1
|
||||
|
||||
/* Register 16h */
|
||||
#define SC8551_REG_16 0x16
|
||||
#define SC8551_IBUS_POL_H_MASK 0x0F
|
||||
#define SC8551_IBUS_ADC_LSB 1.5625
|
||||
|
||||
/* Register 17h */
|
||||
#define SC8551_REG_17 0x17
|
||||
#define SC8551_IBUS_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 18h */
|
||||
#define SC8551_REG_18 0x18
|
||||
#define SC8551_VBUS_POL_H_MASK 0x0F
|
||||
#define SC8551_VBUS_ADC_LSB 3.75
|
||||
|
||||
/* Register 19h */
|
||||
#define SC8551_REG_19 0x19
|
||||
#define SC8551_VBUS_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 1Ah */
|
||||
#define SC8551_REG_1A 0x1A
|
||||
#define SC8551_VAC_POL_H_MASK 0x0F
|
||||
#define SC8551_VAC_ADC_LSB 5
|
||||
|
||||
/* Register 1Bh */
|
||||
#define SC8551_REG_1B 0x1B
|
||||
#define SC8551_VAC_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 1Ch */
|
||||
#define SC8551_REG_1C 0x1C
|
||||
#define SC8551_VOUT_POL_H_MASK 0x0F
|
||||
#define SC8551_VOUT_ADC_LSB 1.25
|
||||
|
||||
/* Register 1Dh */
|
||||
#define SC8551_REG_1D 0x1D
|
||||
#define SC8551_VOUT_POL_L_MASK 0x0F
|
||||
|
||||
/* Register 1Eh */
|
||||
#define SC8551_REG_1E 0x1E
|
||||
#define SC8551_VBAT_POL_H_MASK 0x0F
|
||||
#define SC8551_VBAT_ADC_LSB 1.25
|
||||
|
||||
/* Register 1Fh */
|
||||
#define SC8551_REG_1F 0x1F
|
||||
#define SC8551_VBAT_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 20h */
|
||||
#define SC8551_REG_20 0x20
|
||||
#define SC8551_IBAT_POL_H_MASK 0x0F
|
||||
#define SC8551_IBAT_ADC_LSB 3.125
|
||||
|
||||
/* Register 21h */
|
||||
#define SC8551_REG_21 0x21
|
||||
#define SC8551_IBAT_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 22h */
|
||||
#define SC8551_REG_22 0x22
|
||||
#define SC8551_TSBUS_POL_H_MASK 0x03
|
||||
#define SC8551_TSBUS_ADC_LSB 0.09766
|
||||
|
||||
/* Register 23h */
|
||||
#define SC8551_REG_23 0x23
|
||||
#define SC8551_TSBUS_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 24h */
|
||||
#define SC8551_REG_24 0x24
|
||||
#define SC8551_TSBAT_POL_H_MASK 0x03
|
||||
#define SC8551_TSBAT_ADC_LSB 0.09766
|
||||
|
||||
/* Register 25h */
|
||||
#define SC8551_REG_25 0x25
|
||||
#define SC8551_TSBAT_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 26h */
|
||||
#define SC8551_REG_26 0x26
|
||||
#define SC8551_TDIE_POL_H_MASK 0x01
|
||||
#define SC8551_TDIE_ADC_LSB 0.5
|
||||
|
||||
/* Register 27h */
|
||||
#define SC8551_REG_27 0x27
|
||||
#define SC8551_TDIE_POL_L_MASK 0xFF
|
||||
|
||||
/* Register 28h */
|
||||
#define SC8551_REG_28 0x28
|
||||
#define SC8551_TSBUS_FLT1_MASK 0xFF
|
||||
#define SC8551_TSBUS_FLT1_SHIFT 0
|
||||
#define SC8551_TSBUS_FLT1_BASE 0
|
||||
#define SC8551_TSBUS_FLT1_LSB 0.19531
|
||||
|
||||
/* Register 29h */
|
||||
#define SC8551_REG_29 0x29
|
||||
#define SC8551_TSBAT_FLT0_MASK 0xFF
|
||||
#define SC8551_TSBAT_FLT0_SHIFT 0
|
||||
#define SC8551_TSBAT_FLT0_BASE 0
|
||||
#define SC8551_TSBAT_FLT0_LSB 0.19531
|
||||
|
||||
/* Register 2Ah */
|
||||
#define SC8551_REG_2A 0x2A
|
||||
#define SC8551_TDIE_ALM_MASK 0xFF
|
||||
#define SC8551_TDIE_ALM_SHIFT 0
|
||||
#define SC8551_TDIE_ALM_BASE 25
|
||||
#define SC8551_TDIE_ALM_LSB 5 /*careful multiply is used for calc*/
|
||||
|
||||
/* Register 2Bh */
|
||||
#define SC8551_REG_2B 0x2B
|
||||
#define SC8551_SS_TIMEOUT_SET_MASK 0xE0
|
||||
#define SC8551_SS_TIMEOUT_SET_SHIFT 5
|
||||
#define SC8551_SS_TIMEOUT_DISABLE 0
|
||||
#define SC8551_SS_TIMEOUT_12P5MS 1
|
||||
#define SC8551_SS_TIMEOUT_25MS 2
|
||||
#define SC8551_SS_TIMEOUT_50MS 3
|
||||
#define SC8551_SS_TIMEOUT_100MS 4
|
||||
#define SC8551_SS_TIMEOUT_400MS 5
|
||||
#define SC8551_SS_TIMEOUT_1500MS 6
|
||||
#define SC8551_SS_TIMEOUT_100000MS 7
|
||||
|
||||
#define SC8551_EN_REGULATION_MASK 0x10
|
||||
#define SC8551_EN_REGULATION_SHIFT 4
|
||||
#define SC8551_EN_REGULATION_ENABLE 1
|
||||
#define SC8551_EN_REGULATION_DISABLE 0
|
||||
|
||||
#define SC8551_VOUT_OVP_DIS_MASK 0x08
|
||||
#define SC8551_VOUT_OVP_DIS_SHIFT 3
|
||||
#define SC8551_VOUT_OVP_ENABLE 1
|
||||
#define SC8551_VOUT_OVP_DISABLE 0
|
||||
|
||||
#define SC8551_IBUS_UCP_RISE_TH_MASK 0x04
|
||||
#define SC8551_IBUS_UCP_RISE_TH_SHIFT 2
|
||||
#define SC8551_IBUS_UCP_RISE_150MA 0
|
||||
#define SC8551_IBUS_UCP_RISE_250MA 1
|
||||
|
||||
#define SC8551_SET_IBAT_SNS_RES_MASK 0x02
|
||||
#define SC8551_SET_IBAT_SNS_RES_SHIFT 1
|
||||
#define SC8551_SET_IBAT_SNS_RES_2MHM 0
|
||||
#define SC8551_SET_IBAT_SNS_RES_5MHM 1
|
||||
|
||||
#define SC8551_VAC_PD_EN_MASK 0x01
|
||||
#define SC8551_VAC_PD_EN_SHIFT 0
|
||||
#define SC8551_VAC_PD_ENABLE 1
|
||||
#define SC8551_VAC_PD_DISABLE 0
|
||||
|
||||
/* Register 2Ch */
|
||||
#define SC8551_REG_2C 0x2C
|
||||
#define SC8551_IBAT_REG_MASK 0xC0
|
||||
#define SC8551_IBAT_REG_SHIFT 6
|
||||
#define SC8551_IBAT_REG_200MA 0
|
||||
#define SC8551_IBAT_REG_300MA 1
|
||||
#define SC8551_IBAT_REG_400MA 2
|
||||
#define SC8551_IBAT_REG_500MA 3
|
||||
#define SC8551_VBAT_REG_MASK 0x30
|
||||
#define SC8551_VBAT_REG_SHIFT 4
|
||||
#define SC8551_VBAT_REG_50MV 0
|
||||
#define SC8551_VBAT_REG_100MV 1
|
||||
#define SC8551_VBAT_REG_150MV 2
|
||||
#define SC8551_VBAT_REG_200MV 3
|
||||
|
||||
#define SC8551_VBAT_REG_ACTIVE_STAT_MASK 0x08
|
||||
#define SC8551_IBAT_REG_ACTIVE_STAT_MASK 0x04
|
||||
#define SC8551_VDROP_OVP_ACTIVE_STAT_MASK 0x02
|
||||
#define SC8551_VOUT_OVP_ACTIVE_STAT_MASK 0x01
|
||||
|
||||
#define SC8551_REG_2D 0x2D
|
||||
#define SC8551_VBAT_REG_ACTIVE_FLAG_MASK 0x80
|
||||
#define SC8551_IBAT_REG_ACTIVE_FLAG_MASK 0x40
|
||||
#define SC8551_VDROP_OVP_FLAG_MASK 0x20
|
||||
#define SC8551_VOUT_OVP_FLAG_MASK 0x10
|
||||
#define SC8551_VBAT_REG_ACTIVE_MASK_MASK 0x08
|
||||
#define SC8551_IBAT_REG_ACTIVE_MASK_MASK 0x04
|
||||
#define SC8551_VDROP_OVP_MASK_MASK 0x02
|
||||
#define SC8551_VOUT_OVP_MASK_MASK 0x01
|
||||
|
||||
#define SC8551_REG_2E 0x2E
|
||||
#define SC8551_IBUS_LOW_DG_MASK 0x08
|
||||
#define SC8551_IBUS_LOW_DG_SHIFT 3
|
||||
#define SC8551_IBUS_LOW_DG_10US 0
|
||||
#define SC8551_IBUS_LOW_DG_5MS 1
|
||||
|
||||
#define SC8551_REG_2F 0x2F
|
||||
#define SC8551_PMID2OUT_UVP_FLAG_MASK 0x08
|
||||
#define SC8551_PMID2OUT_OVP_FLAG_MASK 0x04
|
||||
#define SC8551_PMID2OUT_UVP_STAT_MASK 0x02
|
||||
#define SC8551_PMID2OUT_OVP_STAT_MASK 0x01
|
||||
|
||||
#define SC8551_REG_30 0x30
|
||||
#define SC8551_IBUS_REG_EN_MASK 0x80
|
||||
#define SC8551_IBUS_REG_EN_SHIFT 7
|
||||
#define SC8551_IBUS_REG_ENABLE 1
|
||||
#define SC8551_IBUS_REG_DISABLE 0
|
||||
#define SC8551_IBUS_REG_ACTIVE_STAT_MASK 0x40
|
||||
#define SC8551_IBUS_REG_ACTIVE_FLAG_MASK 0x20
|
||||
#define SC8551_IBUS_REG_ACTIVE_MASK_MASK 0x10
|
||||
#define SC8551_IBUS_REG_ACTIVE_MASK_SHIFT 4
|
||||
#define SC8551_IBUS_REG_ACTIVE_NOT_MASK 0
|
||||
#define SC8551_IBUS_REG_ACTIVE_MASK 1
|
||||
#define SC8551_IBUS_REG_MASK 0x0F
|
||||
#define SC8551_IBUS_REG_SHIFT 0
|
||||
#define SC8551_IBUS_REG_BASE 1000
|
||||
#define SC8551_IBUS_REG_LSB 250
|
||||
|
||||
#define SC8551_REG_31 0x31
|
||||
#define SC8551_CHARGE_MODE_MASK 0x01
|
||||
#define SC8551_CHARGE_MODE_SHIFT 0
|
||||
#define SC8551_CHARGE_MODE_2_1 0
|
||||
#define SC8551_CHARGE_MODE_1_1 1
|
||||
|
||||
#define SC8551_REG_34 0x34
|
||||
|
||||
#endif
|
1335
drivers/power/supply/xiaomi/platform/external/cp/ln8000.c
vendored
Normal file
1335
drivers/power/supply/xiaomi/platform/external/cp/ln8000.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
246
drivers/power/supply/xiaomi/platform/external/cp/ln8000_iio.c
vendored
Normal file
246
drivers/power/supply/xiaomi/platform/external/cp/ln8000_iio.c
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
|
||||
#include "inc/ln8000.h"
|
||||
#include "inc/ln8000_reg.h"
|
||||
#include "inc/ln8000_iio.h"
|
||||
|
||||
static int ln_iio_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct ln8000_info *info = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SC_CHARGING_ENABLED:
|
||||
ln_info("POWER_SUPPLY_PROP_CHARGING_ENABLED: %s\n",
|
||||
val1 ? "enable" : "disable");
|
||||
rc = psy_chg_set_charging_enable(info, val1);
|
||||
break;
|
||||
case PSY_IIO_SC_PRESENT:
|
||||
//rc = psy_chg_set_present(info, !!val1);
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported LN8000 IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
pr_err("Couldn't write IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ln_iio_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct ln8000_info *info = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
*val1 = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SC_CHARGING_ENABLED:
|
||||
*val1 = psy_chg_get_charging_enabled(info);
|
||||
break;
|
||||
case PSY_IIO_SC_STATUS:
|
||||
*val1 = 0;
|
||||
break;
|
||||
case PSY_IIO_SC_PRESENT:
|
||||
*val1 = info->usb_present;
|
||||
pr_err("val = %d, usb_present = %d\n", *val1,
|
||||
info->usb_present);
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_PRESENT:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_VBAT, &info->vbat_uV);
|
||||
if (info->vbat_uV > LN8000_ADC_VBAT_MIN) {
|
||||
info->batt_present = 1; /* detected battery */
|
||||
} else {
|
||||
info->batt_present = 0; /* non-detected battery */
|
||||
}
|
||||
*val1 = info->batt_present;
|
||||
break;
|
||||
case PSY_IIO_SC_VBUS_PRESENT:
|
||||
ret = ln8000_check_status(info);
|
||||
*val1 = !(info->vac_unplug);
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_VOLTAGE:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_VBAT, &info->vbat_uV);
|
||||
*val1 = info->vbat_uV / 1000;
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_CURRENT:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_IIN, &info->iin_uA);
|
||||
*val1 = (info->iin_uA * 2) / 1000; /* return to IBUS_ADC x 2 */
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_TEMPERATURE:
|
||||
if (info->pdata->tbat_mon_disable) {
|
||||
*val1 = 0;
|
||||
} else {
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_TSBAT,
|
||||
&info->tbat_uV);
|
||||
*val1 = info->tbat_uV;
|
||||
ln_info("ti_battery_temperature: adc_tbat=%d\n", *val1);
|
||||
}
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_VOLTAGE:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_VIN, &info->vbus_uV);
|
||||
*val1 = info->vbus_uV / 1000;
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_CURRENT:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_IIN, &info->iin_uA);
|
||||
*val1 = info->iin_uA / 1000;
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_TEMPERATURE:
|
||||
if (info->pdata->tbus_mon_disable) {
|
||||
*val1 = 0;
|
||||
} else {
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_TSBUS,
|
||||
&info->tbus_uV);
|
||||
*val1 = info->tbus_uV;
|
||||
ln_info("ti_bus_temperature: adc_tbus=%d\n", *val1);
|
||||
}
|
||||
break;
|
||||
case PSY_IIO_SC_DIE_TEMPERATURE:
|
||||
ln8000_get_adc_data(info, LN8000_ADC_CH_DIETEMP,
|
||||
&info->tdie_dC);
|
||||
*val1 = info->tdie_dC;
|
||||
ln_info("ti_die_temperature: adc_tdie=%d\n", *val1);
|
||||
break;
|
||||
|
||||
case PSY_IIO_SC_ALARM_STATUS:
|
||||
*val1 = psy_chg_get_ti_alarm_status(info);
|
||||
break;
|
||||
case PSY_IIO_SC_FAULT_STATUS:
|
||||
*val1 = psy_chg_get_ti_fault_status(info);
|
||||
break;
|
||||
case PSY_IIO_SC_VBUS_ERROR_STATUS:
|
||||
*val1 = psy_chg_get_it_bus_error_status(info);
|
||||
break;
|
||||
case PSY_IIO_SC_REG_STATUS:
|
||||
ln8000_check_status(info);
|
||||
*val1 = ((info->vbat_regulated << VBAT_REG_STATUS_SHIFT) |
|
||||
/* ln8000 not support ibat_reg, we are can be ibus_reg */
|
||||
(info->iin_regulated << IBAT_REG_STATUS_SHIFT));
|
||||
if (*val1) {
|
||||
ln_info("ln_reg_status: val1=0x%x\n", *val1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("Unsupported QG IIO chan %d\n", chan->channel);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Couldn't read IIO channel %d, ret = %d\n",
|
||||
chan->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ln_iio_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct ln8000_info *chip = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *iio_chan = chip->iio_chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ln8000_iio_psy_channels); i++, iio_chan++)
|
||||
if (iio_chan->channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ln_iio_info = {
|
||||
.read_raw = ln_iio_read_raw,
|
||||
.write_raw = ln_iio_write_raw,
|
||||
.of_xlate = ln_iio_of_xlate,
|
||||
};
|
||||
|
||||
int ln_init_iio_psy(struct ln8000_info *chip)
|
||||
{
|
||||
struct iio_dev *indio_dev = chip->indio_dev;
|
||||
struct iio_chan_spec *chan;
|
||||
int num_iio_channels = ARRAY_SIZE(ln8000_iio_psy_channels);
|
||||
int rc, i;
|
||||
|
||||
pr_err("LN8000 ln_init_iio_psy start\n");
|
||||
chip->iio_chan = devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->iio_chan), GFP_KERNEL);
|
||||
if (!chip->iio_chan)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->int_iio_chans =
|
||||
devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->int_iio_chans), GFP_KERNEL);
|
||||
if (!chip->int_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &ln_iio_info;
|
||||
indio_dev->dev.parent = chip->dev;
|
||||
indio_dev->dev.of_node = chip->dev->of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip->iio_chan;
|
||||
indio_dev->num_channels = num_iio_channels;
|
||||
if (chip->dev_role == LN_ROLE_MASTER) {
|
||||
indio_dev->name = "ln8000-master";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = ln8000_iio_psy_channels[i].channel_num;
|
||||
chan->type = ln8000_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
ln8000_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
ln8000_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
ln8000_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
} else if (chip->dev_role == LN_ROLE_SLAVE) {
|
||||
indio_dev->name = "ln8000-slave";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel =
|
||||
ln8000_slave_iio_psy_channels[i].channel_num;
|
||||
chan->type = ln8000_slave_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
ln8000_slave_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
ln8000_slave_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
ln8000_slave_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
} else {
|
||||
indio_dev->name = "ln8000-standalone";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = ln8000_iio_psy_channels[i].channel_num;
|
||||
chan->type = ln8000_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
ln8000_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
ln8000_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
ln8000_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
}
|
||||
|
||||
rc = devm_iio_device_register(chip->dev, indio_dev);
|
||||
if (rc)
|
||||
pr_err("Failed to register LN8000 IIO device, rc=%d\n", rc);
|
||||
|
||||
pr_err("LN8000 IIO device, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
1670
drivers/power/supply/xiaomi/platform/external/cp/sc8551a.c
vendored
Normal file
1670
drivers/power/supply/xiaomi/platform/external/cp/sc8551a.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
267
drivers/power/supply/xiaomi/platform/external/cp/sc8551a_iio.c
vendored
Normal file
267
drivers/power/supply/xiaomi/platform/external/cp/sc8551a_iio.c
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
|
||||
#include "inc/sc8551a.h"
|
||||
#include "inc/sc8551a_reg.h"
|
||||
#include "inc/sc8551a_iio.h"
|
||||
|
||||
static int sc_iio_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct sc8551 *sc = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SC_CHARGING_ENABLED:
|
||||
sc8551_enable_charge(sc, val1);
|
||||
sc8551_check_charge_enabled(sc, &sc->charge_enabled);
|
||||
sc_info("POWER_SUPPLY_PROP_CHARGING_ENABLED: %s\n",
|
||||
val1 ? "enable" : "disable");
|
||||
break;
|
||||
case PSY_IIO_SC_PRESENT:
|
||||
sc8551_set_present(sc, !!val1);
|
||||
break;
|
||||
case PSY_IIO_SC_ENABLE_ADC:
|
||||
sc8551_enable_adc(sc, !!val1);
|
||||
sc->adc_status = !!val1;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported SC8551 IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
pr_err("Couldn't write IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sc_iio_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct sc8551 *sc = iio_priv(indio_dev);
|
||||
int result = 0;
|
||||
int ret = 0;
|
||||
u8 reg_val;
|
||||
*val1 = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SC_CHARGING_ENABLED:
|
||||
sc8551_check_charge_enabled(sc, &sc->charge_enabled);
|
||||
*val1 = sc->charge_enabled;
|
||||
break;
|
||||
case PSY_IIO_SC_STATUS:
|
||||
*val1 = 0;
|
||||
break;
|
||||
case PSY_IIO_SC_PRESENT:
|
||||
*val1 = 1;
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_PRESENT:
|
||||
ret = sc8551_read_byte(sc, SC8551_REG_0D, ®_val);
|
||||
if (!ret)
|
||||
sc->batt_present = !!(reg_val & VBAT_INSERT);
|
||||
*val1 = sc->batt_present;
|
||||
break;
|
||||
case PSY_IIO_SC_VBUS_PRESENT:
|
||||
ret = sc8551_read_byte(sc, SC8551_REG_0D, ®_val);
|
||||
if (!ret)
|
||||
sc->vbus_present = !!(reg_val & VBUS_INSERT);
|
||||
*val1 = sc->vbus_present;
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_VOLTAGE:
|
||||
ret = sc8551_get_adc_data(sc, ADC_VBAT, &result);
|
||||
if (!ret)
|
||||
sc->vbat_volt = result;
|
||||
*val1 = sc->vbat_volt;
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_CURRENT:
|
||||
ret = sc8551_get_adc_data(sc, ADC_IBAT, &result);
|
||||
if (!ret)
|
||||
sc->ibat_curr = result;
|
||||
|
||||
*val1 = sc->ibat_curr;
|
||||
break;
|
||||
case PSY_IIO_SC_BATTERY_TEMPERATURE:
|
||||
ret = sc8551_get_adc_data(sc, ADC_TBAT, &result);
|
||||
if (!ret)
|
||||
sc->bat_temp = result;
|
||||
|
||||
*val1 = sc->bat_temp;
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_VOLTAGE:
|
||||
ret = sc8551_get_adc_data(sc, ADC_VBUS, &result);
|
||||
if (!ret)
|
||||
sc->vbus_volt = result;
|
||||
|
||||
*val1 = sc->vbus_volt;
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_CURRENT:
|
||||
ret = sc8551_get_adc_data(sc, ADC_IBUS, &result);
|
||||
if (!ret)
|
||||
sc->ibus_curr = result;
|
||||
|
||||
*val1 = sc->ibus_curr;
|
||||
break;
|
||||
case PSY_IIO_SC_BUS_TEMPERATURE:
|
||||
ret = sc8551_get_adc_data(sc, ADC_TBUS, &result);
|
||||
if (!ret)
|
||||
sc->bus_temp = result;
|
||||
|
||||
*val1 = sc->bus_temp;
|
||||
break;
|
||||
case PSY_IIO_SC_DIE_TEMPERATURE:
|
||||
ret = sc8551_get_adc_data(sc, ADC_TDIE, &result);
|
||||
if (!ret)
|
||||
sc->die_temp = result;
|
||||
|
||||
*val1 = sc->die_temp;
|
||||
break;
|
||||
|
||||
case PSY_IIO_SC_ALARM_STATUS:
|
||||
sc8551_check_alarm_status(sc);
|
||||
*val1 = ((sc->bat_ovp_alarm << BAT_OVP_ALARM_SHIFT) |
|
||||
(sc->bat_ocp_alarm << BAT_OCP_ALARM_SHIFT) |
|
||||
(sc->bat_ucp_alarm << BAT_UCP_ALARM_SHIFT) |
|
||||
(sc->bus_ovp_alarm << BUS_OVP_ALARM_SHIFT) |
|
||||
(sc->bus_ocp_alarm << BUS_OCP_ALARM_SHIFT) |
|
||||
(sc->bat_therm_alarm << BAT_THERM_ALARM_SHIFT) |
|
||||
(sc->bus_therm_alarm << BUS_THERM_ALARM_SHIFT) |
|
||||
(sc->die_therm_alarm << DIE_THERM_ALARM_SHIFT));
|
||||
break;
|
||||
case PSY_IIO_SC_FAULT_STATUS:
|
||||
sc8551_check_fault_status(sc);
|
||||
*val1 = ((sc->bat_ovp_fault << BAT_OVP_FAULT_SHIFT) |
|
||||
(sc->bat_ocp_fault << BAT_OCP_FAULT_SHIFT) |
|
||||
(sc->bus_ovp_fault << BUS_OVP_FAULT_SHIFT) |
|
||||
(sc->bus_ocp_fault << BUS_OCP_FAULT_SHIFT) |
|
||||
(sc->bat_therm_fault << BAT_THERM_FAULT_SHIFT) |
|
||||
(sc->bus_therm_fault << BUS_THERM_FAULT_SHIFT) |
|
||||
(sc->die_therm_fault << DIE_THERM_FAULT_SHIFT));
|
||||
break;
|
||||
case PSY_IIO_SC_VBUS_ERROR_STATUS:
|
||||
sc8551_check_vbus_error_status(sc);
|
||||
*val1 = sc->vbus_error;
|
||||
break;
|
||||
case PSY_IIO_SC_ENABLE_ADC:
|
||||
*val1 = sc->adc_status;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported QG IIO chan %d\n", chan->channel);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Couldn't read IIO channel %d, ret = %d\n",
|
||||
chan->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int sc_iio_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct sc8551 *chip = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *iio_chan = chip->iio_chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sc8551_iio_psy_channels); i++, iio_chan++)
|
||||
if (iio_chan->channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info sc_iio_info = {
|
||||
.read_raw = sc_iio_read_raw,
|
||||
.write_raw = sc_iio_write_raw,
|
||||
.of_xlate = sc_iio_of_xlate,
|
||||
};
|
||||
|
||||
int sc_init_iio_psy(struct sc8551 *chip)
|
||||
{
|
||||
struct iio_dev *indio_dev = chip->indio_dev;
|
||||
struct iio_chan_spec *chan;
|
||||
int num_iio_channels = ARRAY_SIZE(sc8551_iio_psy_channels);
|
||||
int rc, i;
|
||||
|
||||
pr_err("SC8551 sc_init_iio_psy start\n");
|
||||
chip->iio_chan = devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->iio_chan), GFP_KERNEL);
|
||||
if (!chip->iio_chan)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->int_iio_chans =
|
||||
devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->int_iio_chans), GFP_KERNEL);
|
||||
if (!chip->int_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &sc_iio_info;
|
||||
indio_dev->dev.parent = chip->dev;
|
||||
indio_dev->dev.of_node = chip->dev->of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip->iio_chan;
|
||||
indio_dev->num_channels = num_iio_channels;
|
||||
if (chip->mode == SC8551_ROLE_MASTER) {
|
||||
indio_dev->name = "sc8551-master";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = sc8551_iio_psy_channels[i].channel_num;
|
||||
chan->type = sc8551_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
sc8551_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
sc8551_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
sc8551_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
} else if (chip->mode == SC8551_ROLE_SLAVE) {
|
||||
indio_dev->name = "sc8551-slave";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel =
|
||||
sc8551_slave_iio_psy_channels[i].channel_num;
|
||||
chan->type = sc8551_slave_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
sc8551_slave_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
sc8551_slave_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
sc8551_slave_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
} else {
|
||||
indio_dev->name = "sc8551-standalone";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = sc8551_iio_psy_channels[i].channel_num;
|
||||
chan->type = sc8551_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
sc8551_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name =
|
||||
sc8551_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
sc8551_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
}
|
||||
|
||||
rc = devm_iio_device_register(chip->dev, indio_dev);
|
||||
if (rc)
|
||||
pr_err("Failed to register SC8551 IIO device, rc=%d\n", rc);
|
||||
|
||||
pr_err("SC8551 IIO device, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
4
drivers/power/supply/xiaomi/platform/external/fg/Makefile
vendored
Normal file
4
drivers/power/supply/xiaomi/platform/external/fg/Makefile
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_FUEL_GAUGE_BQ27Z561) += bq27z561_drv.o
|
||||
bq27z561_drv-objs := bq27z561.o bq27z561_iio.o xm_battery_auth.o xm_soc_smooth.o
|
2294
drivers/power/supply/xiaomi/platform/external/fg/bq27z561.c
vendored
Normal file
2294
drivers/power/supply/xiaomi/platform/external/fg/bq27z561.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
369
drivers/power/supply/xiaomi/platform/external/fg/bq27z561_iio.c
vendored
Normal file
369
drivers/power/supply/xiaomi/platform/external/fg/bq27z561_iio.c
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
|
||||
#include "inc/bq27z561.h"
|
||||
#include "inc/bq27z561_iio.h"
|
||||
#include "inc/xm_battery_auth.h"
|
||||
#include "inc/xm_soc_smooth.h"
|
||||
|
||||
static int fg_iio_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct bq_fg_chip *bq = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_BQFG_TEMP:
|
||||
bq->fake_temp = val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_CAPACITY:
|
||||
bq->fake_soc = val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_UPDATE_NOW:
|
||||
fg_dump_registers(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_THERM_CURR:
|
||||
//bq->fcc_curr = val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_CHIP_OK:
|
||||
bq->fake_chip_ok = !!val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_BATTERY_AUTH:
|
||||
bq->verify_digest_success = !!val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_SHUTDOWN_DELAY:
|
||||
bq->shutdown_delay = val1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_FASTCHARGE_MODE:
|
||||
fg_set_fastcharge_mode(bq, val1);
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported FG IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
pr_err_ratelimited("Couldn't write IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fg_iio_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct bq_fg_chip *bq = iio_priv(indio_dev);
|
||||
static int ov_count;
|
||||
int vbat_mv = 0;
|
||||
static bool shutdown_delay_cancel;
|
||||
static bool last_shutdown_delay;
|
||||
int rc = 0;
|
||||
int ret = 0, status;
|
||||
|
||||
*val1 = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_BQFG_PRESENT:
|
||||
*val1 = 1;
|
||||
break;
|
||||
case PSY_IIO_BQFG_STATUS:
|
||||
status = fg_get_batt_status(bq);
|
||||
if (bq->charge_full)
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
else if (status == POWER_SUPPLY_STATUS_FULL &&
|
||||
!(bq->charge_full)) {
|
||||
if (bq->batt_curr <= 0)
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
*val1 = status;
|
||||
break;
|
||||
case PSY_IIO_BQFG_VOLTAGE_NOW:
|
||||
ret = fg_read_volt(bq);
|
||||
mutex_lock(&bq->data_lock);
|
||||
if (ret >= 0)
|
||||
bq->batt_volt = ret;
|
||||
*val1 = bq->batt_volt * 1000;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
case PSY_IIO_BQFG_VOLTAGE_MAX:
|
||||
*val1 = 4450 * 1000;
|
||||
*val1 = fg_read_charging_voltage(bq);
|
||||
if (*val1 == BQ_MAXIUM_VOLTAGE_FOR_CELL) {
|
||||
if (bq->batt_volt >
|
||||
BQ_PACK_MAXIUM_VOLTAGE_FOR_PMIC_SAFETY) {
|
||||
ov_count++;
|
||||
if (ov_count > 2) {
|
||||
ov_count = 0;
|
||||
bq->cell_ov_check++;
|
||||
}
|
||||
} else {
|
||||
ov_count = 0;
|
||||
}
|
||||
if (bq->cell_ov_check > 4)
|
||||
bq->cell_ov_check = 4;
|
||||
|
||||
*val1 = BQ_PACK_MAXIUM_VOLTAGE_FOR_PMIC -
|
||||
bq->cell_ov_check * 10;
|
||||
if ((bq->batt_soc == 100) &&
|
||||
(*val1 == BQ_PACK_MAXIUM_VOLTAGE_FOR_PMIC))
|
||||
*val1 = BQ_MAXIUM_VOLTAGE_FOR_CELL;
|
||||
}
|
||||
*val1 *= 1000;
|
||||
break;
|
||||
case PSY_IIO_BQFG_CURRENT_NOW:
|
||||
mutex_lock(&bq->data_lock);
|
||||
fg_read_current(bq, &bq->batt_curr);
|
||||
if (bq->batt_curr != BQ_I2C_FAILED_SOC) {
|
||||
bq->old_batt_curr = bq->batt_curr;
|
||||
}
|
||||
if (bq->batt_soc >= 98 && bq->batt_curr == BQ_I2C_FAILED_SOC) {
|
||||
bq->batt_curr = bq->old_batt_curr;
|
||||
}
|
||||
*val1 = bq->batt_curr * 1000;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
case PSY_IIO_BQFG_CAPACITY:
|
||||
//add shutdown delay feature
|
||||
if (bq->fake_soc >= 0) {
|
||||
*val1 = bq->fake_soc;
|
||||
if (bq->batt_volt < 3450) {
|
||||
*val1 = 1;
|
||||
bq->fake_soc = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (bq->batt_soc < 0)
|
||||
bq->batt_soc = bq->batt_soc_old;
|
||||
else
|
||||
bq->batt_soc_old = bq->batt_soc;
|
||||
*val1 = bq->batt_soc;
|
||||
|
||||
if (bq->shutdown_delay_enable) {
|
||||
if (*val1 == 0) {
|
||||
vbat_mv = fg_read_volt(bq);
|
||||
if (g_battmngr_noti->mainchg_msg.chg_plugin ||
|
||||
g_battmngr_noti->pd_msg.pd_active)
|
||||
status = 1;
|
||||
else
|
||||
status = 0;
|
||||
if (vbat_mv > SHUTDOWN_DELAY_VOL && !status) {
|
||||
bq->shutdown_delay = true;
|
||||
*val1 = 1;
|
||||
} else if (status && bq->shutdown_delay) {
|
||||
bq->shutdown_delay = false;
|
||||
shutdown_delay_cancel = true;
|
||||
*val1 = 1;
|
||||
} else {
|
||||
bq->shutdown_delay = false;
|
||||
if (vbat_mv > SHUTDOWN_DELAY_VOL + 20)
|
||||
*val1 = 1;
|
||||
}
|
||||
} else {
|
||||
bq->shutdown_delay = false;
|
||||
shutdown_delay_cancel = false;
|
||||
}
|
||||
|
||||
if (last_shutdown_delay != bq->shutdown_delay) {
|
||||
last_shutdown_delay = bq->shutdown_delay;
|
||||
if (bq->batt_psy)
|
||||
power_supply_changed(bq->batt_psy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PSY_IIO_BQFG_CAPACITY_LEVEL:
|
||||
*val1 = fg_get_batt_capacity_level(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_TEMP:
|
||||
if (bq->fake_temp != -EINVAL) {
|
||||
*val1 = bq->fake_temp;
|
||||
break;
|
||||
}
|
||||
*val1 = bq->batt_temp;
|
||||
break;
|
||||
case PSY_IIO_BQFG_TIME_TO_EMPTY_NOW:
|
||||
ret = fg_read_tte(bq);
|
||||
mutex_lock(&bq->data_lock);
|
||||
if (ret >= 0)
|
||||
bq->batt_tte = ret;
|
||||
|
||||
*val1 = bq->batt_tte * 60;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
|
||||
case PSY_IIO_BQFG_CHARGE_FULL:
|
||||
ret = fg_read_fcc(bq);
|
||||
mutex_lock(&bq->data_lock);
|
||||
if (ret > 0)
|
||||
bq->batt_fcc = ret;
|
||||
*val1 = bq->batt_fcc * 1000;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
|
||||
case PSY_IIO_BQFG_CHARGE_FULL_DESIGN:
|
||||
ret = fg_read_dc(bq);
|
||||
mutex_lock(&bq->data_lock);
|
||||
if (ret > 0)
|
||||
bq->batt_dc = ret;
|
||||
*val1 = bq->batt_dc * 1000;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
|
||||
case PSY_IIO_BQFG_CYCLE_COUNT:
|
||||
ret = fg_read_cyclecount(bq);
|
||||
mutex_lock(&bq->data_lock);
|
||||
if (ret >= 0)
|
||||
bq->batt_cyclecnt = ret;
|
||||
*val1 = bq->batt_cyclecnt;
|
||||
mutex_unlock(&bq->data_lock);
|
||||
break;
|
||||
case PSY_IIO_BQFG_TIME_TO_FULL_NOW:
|
||||
*val1 = fg_read_ttf(bq) * 60;
|
||||
break;
|
||||
|
||||
case PSY_IIO_BQFG_RESISTANCE_ID:
|
||||
*val1 = 10000;
|
||||
break;
|
||||
case PSY_IIO_BQFG_UPDATE_NOW:
|
||||
*val1 = 0;
|
||||
break;
|
||||
case PSY_IIO_BQFG_THERM_CURR:
|
||||
//*val1 = bq->fcc_curr;
|
||||
break;
|
||||
case PSY_IIO_BQFG_CHIP_OK:
|
||||
if (bq->fake_chip_ok != -EINVAL) {
|
||||
*val1 = bq->fake_chip_ok;
|
||||
break;
|
||||
}
|
||||
*val1 = fg_get_chip_ok(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_BATTERY_AUTH:
|
||||
*val1 = bq->verify_digest_success;
|
||||
break;
|
||||
case PSY_IIO_BQFG_SOC_DECIMAL:
|
||||
*val1 = fg_get_soc_decimal(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_SOC_DECIMAL_RATE:
|
||||
*val1 = fg_get_soc_decimal_rate(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_SOH:
|
||||
*val1 = fg_read_soh(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_BATTERY_ID:
|
||||
*val1 = bq->batt_id;
|
||||
break;
|
||||
case PSY_IIO_BQFG_RSOC:
|
||||
*val1 = (bq->batt_rm * 10000) / bq->batt_fcc;
|
||||
break;
|
||||
case PSY_IIO_BQFG_SHUTDOWN_DELAY:
|
||||
*val1 = bq->shutdown_delay;
|
||||
pr_err("vbat_mv %d bq->shutdown_delay = %d, shutdown_delay_enable = %d, batt_soc = %d\n",
|
||||
bq->batt_volt, bq->shutdown_delay,
|
||||
bq->shutdown_delay_enable, bq->batt_soc);
|
||||
break;
|
||||
case PSY_IIO_BQFG_FASTCHARGE_MODE:
|
||||
*val1 = bq->fast_mode;
|
||||
break;
|
||||
case PSY_IIO_BQFG_TEMP_MAX:
|
||||
*val1 = fg_get_temp_max_fac(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_TIME_OT:
|
||||
*val1 = fg_get_time_ot(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_REG_RSOC:
|
||||
*val1 = fg_read_rsoc(bq);
|
||||
break;
|
||||
case PSY_IIO_BQFG_RM:
|
||||
*val1 = bq->batt_rm * 1000;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported FG IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
pr_err_ratelimited("Couldn't read IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int fg_iio_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct bq_fg_chip *chip = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *iio_chan = chip->iio_chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bq27z561_iio_psy_channels); i++, iio_chan++)
|
||||
if (iio_chan->channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info fg_iio_info = {
|
||||
.read_raw = fg_iio_read_raw,
|
||||
.write_raw = fg_iio_write_raw,
|
||||
.of_xlate = fg_iio_of_xlate,
|
||||
};
|
||||
|
||||
int bq27z561_init_iio_psy(struct bq_fg_chip *chip)
|
||||
{
|
||||
struct iio_dev *indio_dev = chip->indio_dev;
|
||||
struct iio_chan_spec *chan;
|
||||
int fg_num_iio_channels = ARRAY_SIZE(bq27z561_iio_psy_channels);
|
||||
int rc, i;
|
||||
|
||||
chip->iio_chan = devm_kcalloc(chip->dev, fg_num_iio_channels,
|
||||
sizeof(*chip->iio_chan), GFP_KERNEL);
|
||||
if (!chip->iio_chan)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->int_iio_chans =
|
||||
devm_kcalloc(chip->dev, fg_num_iio_channels,
|
||||
sizeof(*chip->int_iio_chans), GFP_KERNEL);
|
||||
if (!chip->int_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->ext_iio_chans =
|
||||
devm_kcalloc(chip->dev, ARRAY_SIZE(bq27z561_iio_psy_channels),
|
||||
sizeof(*chip->ext_iio_chans), GFP_KERNEL);
|
||||
if (!chip->ext_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &fg_iio_info;
|
||||
indio_dev->dev.parent = chip->dev;
|
||||
indio_dev->dev.of_node = chip->dev->of_node;
|
||||
indio_dev->name = "bq27z561,fg";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip->iio_chan;
|
||||
indio_dev->num_channels = fg_num_iio_channels;
|
||||
|
||||
for (i = 0; i < fg_num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = bq27z561_iio_psy_channels[i].channel_num;
|
||||
chan->type = bq27z561_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
bq27z561_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name = bq27z561_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
bq27z561_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
|
||||
rc = devm_iio_device_register(chip->dev, indio_dev);
|
||||
if (rc)
|
||||
pr_err("Failed to register FG IIO device, rc=%d\n", rc);
|
||||
|
||||
pr_err("BQ27z561 IIO device, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
415
drivers/power/supply/xiaomi/platform/external/fg/inc/bq27z561.h
vendored
Normal file
415
drivers/power/supply/xiaomi/platform/external/fg/inc/bq27z561.h
vendored
Normal file
@ -0,0 +1,415 @@
|
||||
|
||||
#ifndef __BQ27Z561_H
|
||||
#define __BQ27Z561_H
|
||||
|
||||
#define pr_fmt(fmt) "[bq27z561] %s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
#include <linux/battmngr/battmngr_voter.h>
|
||||
|
||||
enum print_reason {
|
||||
PR_INTERRUPT = BIT(0),
|
||||
PR_REGISTER = BIT(1),
|
||||
PR_OEM = BIT(2),
|
||||
PR_DEBUG = BIT(3),
|
||||
};
|
||||
|
||||
#define PROBE_CNT_MAX 50
|
||||
|
||||
#define INVALID_REG_ADDR 0xFF
|
||||
|
||||
#define FG_FLAGS_FD BIT(4)
|
||||
#define FG_FLAGS_FC BIT(5)
|
||||
#define FG_FLAGS_DSG BIT(6)
|
||||
#define FG_FLAGS_RCA BIT(9)
|
||||
#define FG_FLAGS_FASTCHAGE BIT(5)
|
||||
|
||||
#define BATTERY_DIGEST_LEN 32
|
||||
|
||||
#define DEFUALT_FULL_DESIGN 5000
|
||||
|
||||
#define BQ_REPORT_FULL_SOC 9800
|
||||
#define BQ_CHARGE_FULL_SOC 9750
|
||||
#define BQ_RECHARGE_SOC 9800
|
||||
#define BQ_DEFUALT_FULL_SOC 100
|
||||
|
||||
#define BATT_NORMAL_H_THRESHOLD 350
|
||||
|
||||
#define TI_TERM_CURRENT_L 1100
|
||||
#define TI_TERM_CURRENT_H 1232
|
||||
|
||||
#define NFG_TERM_CURRENT_L 1237
|
||||
#define NFG_TERM_CURRENT_H 1458
|
||||
|
||||
#define BQ27Z561_DEFUALT_TERM -200
|
||||
#define BQ27Z561_DEFUALT_FFC_TERM -680
|
||||
#define BQ27Z561_DEFUALT_RECHARGE_VOL 4380
|
||||
|
||||
#define NO_FFC_OVER_FV (4464 * 1000)
|
||||
#define STEP_FV (16 * 1000)
|
||||
#define BATT_WARM_THRESHOLD 480
|
||||
|
||||
#define PD_CHG_UPDATE_DELAY_US 20 /*20 sec*/
|
||||
#define BQ_I2C_FAILED_SOC -107
|
||||
#define BQ_I2C_FAILED_TEMP -307
|
||||
#define DEBUG_CAPATICY 15
|
||||
#define BMS_FG_VERIFY "BMS_FG_VERIFY"
|
||||
#define CC_CV_STEP "CC_CV_STEP"
|
||||
#define FCCMAIN_FG_CHARGE_FULL_VOTER "FCCMAIN_FG_CHARGE_FULL_VOTER"
|
||||
#define OVER_FV_VOTER "OVER_FV_VOTER"
|
||||
#define SMART_BATTERY_FV "SMART_BATTERY_FV"
|
||||
|
||||
#define BQ_PACK_MAXIUM_VOLTAGE_FOR_PMIC 4490
|
||||
#define BQ_MAXIUM_VOLTAGE_FOR_CELL 4480
|
||||
#define BQ_PACK_MAXIUM_VOLTAGE_FOR_PMIC_SAFETY 4477
|
||||
enum bq_fg_reg_idx {
|
||||
BQ_FG_REG_CTRL = 0,
|
||||
BQ_FG_REG_TEMP, /* Battery Temperature */
|
||||
BQ_FG_REG_VOLT, /* Battery Voltage */
|
||||
BQ_FG_REG_CN, /* Current Now */
|
||||
BQ_FG_REG_AI, /* Average Current */
|
||||
BQ_FG_REG_BATT_STATUS, /* BatteryStatus */
|
||||
BQ_FG_REG_TTE, /* Time to Empty */
|
||||
BQ_FG_REG_TTF, /* Time to Full */
|
||||
BQ_FG_REG_FCC, /* Full Charge Capacity */
|
||||
BQ_FG_REG_RM, /* Remaining Capacity */
|
||||
BQ_FG_REG_CC, /* Cycle Count */
|
||||
BQ_FG_REG_SOC, /* Relative State of Charge */
|
||||
BQ_FG_REG_SOH, /* State of Health */
|
||||
BQ_FG_REG_CHG_VOL, /* Charging Voltage*/
|
||||
BQ_FG_REG_CHG_CUR, /* Charging Current*/
|
||||
BQ_FG_REG_DC, /* Design Capacity */
|
||||
BQ_FG_REG_ALT_MAC, /* AltManufactureAccess*/
|
||||
BQ_FG_REG_MAC_DATA, /* MACData*/
|
||||
BQ_FG_REG_MAC_CHKSUM, /* MACChecksum */
|
||||
BQ_FG_REG_MAC_DATA_LEN, /* MACDataLen */
|
||||
NUM_REGS,
|
||||
};
|
||||
|
||||
static u8 bq27z561_regs[NUM_REGS] = {
|
||||
0x00, /* CONTROL */
|
||||
0x06, /* TEMP */
|
||||
0x08, /* VOLT */
|
||||
0x0C, /* CURRENT NOW */
|
||||
0x14, /* AVG CURRENT */
|
||||
0x0A, /* FLAGS */
|
||||
0x16, /* Time to empty */
|
||||
0x18, /* Time to full */
|
||||
0x12, /* Full charge capacity */
|
||||
0x10, /* Remaining Capacity */
|
||||
0x2A, /* CycleCount */
|
||||
0x2C, /* State of Charge */
|
||||
0x2E, /* State of Health */
|
||||
0x30, /* Charging Voltage*/
|
||||
0x32, /* Charging Current*/
|
||||
0x3C, /* Design Capacity */
|
||||
0x3E, /* AltManufacturerAccess*/
|
||||
0x40, /* MACData*/
|
||||
0x60, /* MACChecksum */
|
||||
0x61, /* MACDataLen */
|
||||
};
|
||||
|
||||
enum bq_fg_mac_cmd {
|
||||
FG_MAC_CMD_CTRL_STATUS = 0x0000,
|
||||
FG_MAC_CMD_DEV_TYPE = 0x0001,
|
||||
FG_MAC_CMD_FW_VER = 0x0002,
|
||||
FG_MAC_CMD_HW_VER = 0x0003,
|
||||
FG_MAC_CMD_IF_SIG = 0x0004,
|
||||
FG_MAC_CMD_CHEM_ID = 0x0006,
|
||||
FG_MAC_CMD_GAUGING = 0x0021,
|
||||
FG_MAC_CMD_SEAL = 0x0030,
|
||||
FG_MAC_CMD_FASTCHARGE_EN = 0x003E,
|
||||
FG_MAC_CMD_FASTCHARGE_DIS = 0x003F,
|
||||
FG_MAC_CMD_DEV_RESET = 0x0041,
|
||||
FG_MAC_CMD_CHEM_NAME = 0x004B,
|
||||
FG_MAC_CMD_MANU_NAME = 0x004C,
|
||||
FG_MAC_CMD_CHARGING_STATUS = 0x0055,
|
||||
FG_MAC_CMD_GAGUE_STATUS = 0x0056,
|
||||
FG_MAC_CMD_LIFETIME1 = 0x0060,
|
||||
FG_MAC_CMD_LIFETIME3 = 0x0062,
|
||||
FG_MAC_CMD_ITSTATUS1 = 0x0073,
|
||||
FG_MAC_CMD_QMAX = 0x0075,
|
||||
FG_MAC_CMD_FCC_SOH = 0x0077,
|
||||
FG_MAC_CMD_RA_TABLE = 0x40C0,
|
||||
FG_MAC_CMD_TERM_CURRENTS = 0x456E,
|
||||
};
|
||||
|
||||
enum {
|
||||
SEAL_STATE_RSVED,
|
||||
SEAL_STATE_UNSEALED,
|
||||
SEAL_STATE_SEALED,
|
||||
SEAL_STATE_FA,
|
||||
};
|
||||
|
||||
enum bq_fg_device {
|
||||
BQ27Z561 = 0,
|
||||
BQ28Z610,
|
||||
};
|
||||
|
||||
static const unsigned char *device2str[] = {
|
||||
"bq27z561",
|
||||
"bq28z610",
|
||||
};
|
||||
|
||||
struct cold_thermal {
|
||||
int index;
|
||||
int temp_l;
|
||||
int temp_h;
|
||||
int curr_th;
|
||||
};
|
||||
|
||||
#define STEP_TABLE_MAX 3
|
||||
struct step_config {
|
||||
int volt_lim;
|
||||
int curr_lim;
|
||||
};
|
||||
|
||||
/* for test
|
||||
struct step_config cc_cv_step_config[STEP_TABLE_MAX] = {
|
||||
{4150-2, 5500},
|
||||
{4190-1, 4200},
|
||||
{4225-1, 3000},
|
||||
};*/
|
||||
|
||||
static struct step_config cc_cv_step_config[STEP_TABLE_MAX] = {
|
||||
{ 4200 - 3, 8820 },
|
||||
{ 4300 - 3, 7350 },
|
||||
{ 4400 - 3, 5880 },
|
||||
};
|
||||
|
||||
enum bq_fg_part_no {
|
||||
TIBQ27Z561 = 0,
|
||||
NFG1000B,
|
||||
};
|
||||
|
||||
struct bq_fg_chip {
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct iio_channel *int_iio_chans;
|
||||
struct iio_channel **ext_iio_chans;
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct votable *fv_votable;
|
||||
struct votable *smart_batt_votable;
|
||||
struct votable *usb_icl_votable;
|
||||
struct votable *fcc_votable;
|
||||
|
||||
struct mutex i2c_rw_lock;
|
||||
struct mutex data_lock;
|
||||
enum bq_fg_part_no fg_device;
|
||||
|
||||
int fw_ver;
|
||||
int df_ver;
|
||||
|
||||
u8 chip;
|
||||
u8 regs[NUM_REGS];
|
||||
char *model_name;
|
||||
|
||||
/* status tracking */
|
||||
|
||||
bool batt_fc;
|
||||
bool batt_fd; /* full depleted */
|
||||
|
||||
bool batt_dsg;
|
||||
bool batt_rca; /* remaining capacity alarm */
|
||||
|
||||
bool batt_fc_1;
|
||||
bool batt_fd_1; /* full depleted */
|
||||
bool batt_tc_1;
|
||||
bool batt_td_1; /* full depleted */
|
||||
|
||||
int seal_state; /* 0 - Full Access, 1 - Unsealed, 2 - Sealed */
|
||||
int batt_tte;
|
||||
int batt_soc;
|
||||
int batt_rsoc;
|
||||
int batt_soc_old;
|
||||
int batt_soc_flag;
|
||||
int batt_fcc; /* Full charge capacity */
|
||||
int batt_rm; /* Remaining capacity */
|
||||
int batt_dc; /* Design Capacity */
|
||||
int batt_volt;
|
||||
int batt_temp;
|
||||
int old_batt_temp;
|
||||
int batt_curr;
|
||||
int old_batt_curr;
|
||||
int fcc_curr;
|
||||
int batt_resistance;
|
||||
int batt_cyclecnt; /* cycle count */
|
||||
int batt_st;
|
||||
int raw_soc;
|
||||
int last_soc;
|
||||
int batt_id;
|
||||
int Qmax_old;
|
||||
int rm_adjust;
|
||||
int rm_adjust_max;
|
||||
bool rm_flag;
|
||||
|
||||
/* debug */
|
||||
int skip_reads;
|
||||
int skip_writes;
|
||||
|
||||
int fake_soc;
|
||||
int fake_temp;
|
||||
int fake_volt;
|
||||
int fake_chip_ok;
|
||||
int retry_chip_ok;
|
||||
int count;
|
||||
int last_count;
|
||||
|
||||
struct delayed_work monitor_work;
|
||||
//struct power_supply *fg_psy;
|
||||
struct power_supply *usb_psy;
|
||||
struct power_supply *batt_psy;
|
||||
//struct power_supply_desc fg_psy_d;
|
||||
//struct timeval *suspend_time;
|
||||
ktime_t suspend_time;
|
||||
|
||||
u8 digest[BATTERY_DIGEST_LEN];
|
||||
bool verify_digest_success;
|
||||
int constant_charge_current_max;
|
||||
|
||||
bool charge_done;
|
||||
bool charge_full;
|
||||
int health;
|
||||
int batt_recharge_vol;
|
||||
bool recharge_done_flag;
|
||||
/* workaround for debug or other purpose */
|
||||
bool ignore_digest_for_debug;
|
||||
bool old_hw;
|
||||
|
||||
int *dec_rate_seq;
|
||||
int dec_rate_len;
|
||||
|
||||
struct cold_thermal *cold_thermal_seq;
|
||||
int cold_thermal_len;
|
||||
bool update_now;
|
||||
bool resume_update;
|
||||
bool fast_mode;
|
||||
int optimiz_soc;
|
||||
bool ffc_smooth;
|
||||
bool shutdown_delay;
|
||||
bool shutdown_delay_enable;
|
||||
|
||||
int cell1_max;
|
||||
int max_charge_current;
|
||||
int max_discharge_current;
|
||||
int max_temp_cell;
|
||||
int min_temp_cell;
|
||||
int total_fw_runtime;
|
||||
int time_spent_in_lt;
|
||||
int time_spent_in_ht;
|
||||
int time_spent_in_ot;
|
||||
|
||||
int cell_ov_check;
|
||||
};
|
||||
|
||||
enum manu_macro {
|
||||
TERMINATION = 0,
|
||||
RECHARGE_VOL,
|
||||
FFC_TERMINATION,
|
||||
MANU_NAME,
|
||||
MANU_DATA_LEN,
|
||||
};
|
||||
|
||||
#define TERMINATION_BYTE 6
|
||||
#define TERMINATION_BASE 30
|
||||
#define TERMINATION_STEP 5
|
||||
|
||||
#define RECHARGE_VOL_BYTE 7
|
||||
#define RECHARGE_VOL_BASE 4200
|
||||
#define RECHARGE_VOL_STEP 5
|
||||
|
||||
#define FFC_TERMINATION_BYTE 8
|
||||
#define FFC_TERMINATION_BASE 400
|
||||
#define FFC_TERMINATION_STEP 20
|
||||
|
||||
#define MANU_NAME_BYTE 3
|
||||
#define MANU_NAME_BASE 0x0C
|
||||
#define MANU_NAME_STEP 1
|
||||
|
||||
struct manu_data {
|
||||
int byte;
|
||||
int base;
|
||||
int step;
|
||||
int data;
|
||||
};
|
||||
|
||||
static struct manu_data manu_info[MANU_DATA_LEN] = {
|
||||
{ TERMINATION_BYTE, TERMINATION_BASE, TERMINATION_STEP },
|
||||
{ RECHARGE_VOL_BYTE, RECHARGE_VOL_BASE, RECHARGE_VOL_STEP },
|
||||
{ FFC_TERMINATION_BYTE, FFC_TERMINATION_BASE, FFC_TERMINATION_STEP },
|
||||
{ MANU_NAME, MANU_NAME_BASE, MANU_NAME_STEP },
|
||||
};
|
||||
|
||||
#define SHUTDOWN_DELAY_VOL 3300
|
||||
#define BQ_RESUME_UPDATE_TIME 600
|
||||
|
||||
static const u8 fg_dump_regs[] = {
|
||||
0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x16, 0x18, 0x1A,
|
||||
0x1C, 0x1E, 0x20, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x66, 0x68, 0x6C, 0x6E,
|
||||
};
|
||||
|
||||
extern struct bq_fg_chip *g_bq27z561;
|
||||
|
||||
int fg_write_byte(struct bq_fg_chip *bq, u8 reg, u8 val);
|
||||
int fg_read_word(struct bq_fg_chip *bq, u8 reg, u16 *val);
|
||||
int fg_read_block(struct bq_fg_chip *bq, u8 reg, u8 *buf, u8 len);
|
||||
int fg_write_block(struct bq_fg_chip *bq, u8 reg, u8 *data, u8 len);
|
||||
u8 checksum(u8 *data, u8 len);
|
||||
int fg_mac_read_block(struct bq_fg_chip *bq, u16 cmd, u8 *buf, u8 len);
|
||||
int fg_mac_write_block(struct bq_fg_chip *bq, u16 cmd, u8 *data, u8 len);
|
||||
int fg_check_battery_psy(struct bq_fg_chip *bq);
|
||||
int fg_get_gague_mode(struct bq_fg_chip *bq);
|
||||
int fg_set_fastcharge_mode(struct bq_fg_chip *bq, bool enable);
|
||||
int fg_read_status(struct bq_fg_chip *bq);
|
||||
int fg_get_manufacture_data(struct bq_fg_chip *bq);
|
||||
int fg_get_chem_data(struct bq_fg_chip *bq);
|
||||
int fg_read_rsoc(struct bq_fg_chip *bq);
|
||||
int fg_read_system_soc(struct bq_fg_chip *bq);
|
||||
int fg_read_temperature(struct bq_fg_chip *bq);
|
||||
int fg_read_volt(struct bq_fg_chip *bq);
|
||||
int fg_read_avg_current(struct bq_fg_chip *bq, int *curr);
|
||||
int fg_read_current(struct bq_fg_chip *bq, int *curr);
|
||||
int fg_read_fcc(struct bq_fg_chip *bq);
|
||||
int fg_read_dc(struct bq_fg_chip *bq);
|
||||
int fg_read_rm(struct bq_fg_chip *bq);
|
||||
int fg_read_soh(struct bq_fg_chip *bq);
|
||||
int fg_read_cyclecount(struct bq_fg_chip *bq);
|
||||
int fg_read_tte(struct bq_fg_chip *bq);
|
||||
int fg_read_charging_voltage(struct bq_fg_chip *bq);
|
||||
int fg_get_temp_max_fac(struct bq_fg_chip *bq);
|
||||
int fg_get_time_ot(struct bq_fg_chip *bq);
|
||||
int fg_read_ttf(struct bq_fg_chip *bq);
|
||||
int fg_get_chip_ok(struct bq_fg_chip *bq);
|
||||
int fg_get_batt_status(struct bq_fg_chip *bq);
|
||||
int fg_get_batt_capacity_level(struct bq_fg_chip *bq);
|
||||
int fg_get_soc_decimal_rate(struct bq_fg_chip *bq);
|
||||
int fg_get_soc_decimal(struct bq_fg_chip *bq);
|
||||
int fg_dump_registers(struct bq_fg_chip *bq);
|
||||
int fg_get_lifetime_data(struct bq_fg_chip *bq);
|
||||
void fg_update_status(struct bq_fg_chip *bq);
|
||||
int fg_get_raw_soc(struct bq_fg_chip *bq);
|
||||
int fg_update_charge_full(struct bq_fg_chip *bq);
|
||||
int calc_delta_time(ktime_t time_last, int *delta_time);
|
||||
|
||||
#endif /* __BQ27Z561_H */
|
112
drivers/power/supply/xiaomi/platform/external/fg/inc/bq27z561_iio.h
vendored
Normal file
112
drivers/power/supply/xiaomi/platform/external/fg/inc/bq27z561_iio.h
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __BQ27Z561_IIO_H
|
||||
#define __BQ27Z561_IIO_H
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/qti_power_supply.h>
|
||||
#include <dt-bindings/iio/qti_power_supply_iio.h>
|
||||
|
||||
struct bq27z561_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define FG_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define FG_CHAN_VOLT(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_CUR(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_RES(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_TEMP(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_POW(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_POWER, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_ENERGY(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_ENERGY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_INDEX(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_INDEX, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_ACT(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_ACTIVITY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_TSTAMP(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_TIMESTAMP, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define FG_CHAN_COUNT(_name, _num) \
|
||||
FG_IIO_CHAN(_name, _num, IIO_COUNT, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
static const struct bq27z561_iio_channels bq27z561_iio_psy_channels[] = {
|
||||
FG_CHAN_ENERGY("bqfg_present", PSY_IIO_BQFG_PRESENT) FG_CHAN_ENERGY(
|
||||
"bqfg_status",
|
||||
PSY_IIO_BQFG_STATUS) FG_CHAN_VOLT("bqfg_voltage_now",
|
||||
PSY_IIO_BQFG_VOLTAGE_NOW) FG_CHAN_VOLT("bqfg_voltage_max",
|
||||
PSY_IIO_BQFG_VOLTAGE_MAX)
|
||||
FG_CHAN_CUR("bqfg_current_now", PSY_IIO_BQFG_CURRENT_NOW) FG_CHAN_ENERGY(
|
||||
"bqfg_capacity",
|
||||
PSY_IIO_BQFG_CAPACITY) FG_CHAN_ENERGY("bqfg_capacity_level",
|
||||
PSY_IIO_BQFG_CAPACITY_LEVEL)
|
||||
FG_CHAN_TEMP("bqfg_temp", PSY_IIO_BQFG_TEMP) FG_CHAN_ENERGY(
|
||||
"bqfg_charge_full",
|
||||
PSY_IIO_BQFG_CHARGE_FULL) FG_CHAN_ENERGY("bqfg_charge_full_design",
|
||||
PSY_IIO_BQFG_CHARGE_FULL_DESIGN)
|
||||
FG_CHAN_COUNT("bqfg_cycle_count", PSY_IIO_BQFG_CYCLE_COUNT) FG_CHAN_ENERGY(
|
||||
"bqfg_time_to_empty_now",
|
||||
PSY_IIO_BQFG_TIME_TO_EMPTY_NOW) FG_CHAN_TSTAMP("bqfg_time_to_full_now",
|
||||
PSY_IIO_BQFG_TIME_TO_FULL_NOW)
|
||||
FG_CHAN_ENERGY("bqfg_update_now", PSY_IIO_BQFG_UPDATE_NOW) FG_CHAN_CUR(
|
||||
"bqfg_therm_curr",
|
||||
PSY_IIO_BQFG_THERM_CURR) FG_CHAN_CUR("bqfg_chip_ok",
|
||||
PSY_IIO_BQFG_CHIP_OK)
|
||||
FG_CHAN_CUR("bqfg_battery_auth", PSY_IIO_BQFG_BATTERY_AUTH) FG_CHAN_CUR(
|
||||
"bqfg_soc_decimal",
|
||||
PSY_IIO_BQFG_SOC_DECIMAL) FG_CHAN_CUR("bqfg_soc_decimal_rate",
|
||||
PSY_IIO_BQFG_SOC_DECIMAL_RATE)
|
||||
FG_CHAN_CUR("bqfg_soh", PSY_IIO_BQFG_SOH) FG_CHAN_CUR(
|
||||
"bqfg_rsoc",
|
||||
PSY_IIO_BQFG_RSOC) FG_CHAN_CUR("bqfg_battery_id",
|
||||
PSY_IIO_BQFG_BATTERY_ID)
|
||||
FG_CHAN_RES("bqfg_resistance_id", PSY_IIO_BQFG_RESISTANCE_ID) FG_CHAN_VOLT(
|
||||
"bqfg_shutdown_delay",
|
||||
PSY_IIO_BQFG_SHUTDOWN_DELAY)
|
||||
FG_CHAN_VOLT(
|
||||
"bqfg_fastcharge_mode",
|
||||
PSY_IIO_BQFG_FASTCHARGE_MODE)
|
||||
FG_CHAN_TEMP(
|
||||
"bqfg_temp_max",
|
||||
PSY_IIO_BQFG_TEMP_MAX)
|
||||
FG_CHAN_TSTAMP(
|
||||
"bqfg_time_ot",
|
||||
PSY_IIO_BQFG_TIME_OT)
|
||||
FG_CHAN_CUR(
|
||||
"bqfg_reg_rsoc",
|
||||
PSY_IIO_BQFG_REG_RSOC)
|
||||
FG_CHAN_CUR(
|
||||
"bqfg_rm",
|
||||
PSY_IIO_BQFG_RM)
|
||||
};
|
||||
|
||||
int bq27z561_init_iio_psy(struct bq_fg_chip *chip);
|
||||
|
||||
#endif /* __BQ27Z561_IIO_H */
|
8
drivers/power/supply/xiaomi/platform/external/fg/inc/xm_battery_auth.h
vendored
Normal file
8
drivers/power/supply/xiaomi/platform/external/fg/inc/xm_battery_auth.h
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
#ifndef __XM_BATTERY_AUTH_H
|
||||
#define __XM_BATTERY_AUTH_H
|
||||
|
||||
int StringToHex(char *str, unsigned char *out, unsigned int *outlen);
|
||||
int fg_sha256_auth(struct bq_fg_chip *bq, u8 *rand_num, int length);
|
||||
|
||||
#endif /* __XM_BATTERY_AUTH_H */
|
30
drivers/power/supply/xiaomi/platform/external/fg/inc/xm_soc_smooth.h
vendored
Normal file
30
drivers/power/supply/xiaomi/platform/external/fg/inc/xm_soc_smooth.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#ifndef __XM_SOC_SMOOTH_H
|
||||
#define __XM_SOC_SMOOTH_H
|
||||
|
||||
#define BATT_HIGH_AVG_CURRENT 1000000
|
||||
#define NORMAL_TEMP_CHARGING_DELTA 10000
|
||||
#define NORMAL_DISTEMP_CHARGING_DELTA 60000
|
||||
#define LOW_TEMP_CHARGING_DELTA 20000
|
||||
#define LOW_TEMP_DISCHARGING_DELTA 40000
|
||||
#define FFC_SMOOTH_LEN 4
|
||||
#define FG_RAW_SOC_FULL 10000
|
||||
#define FG_REPORT_FULL_SOC 9400
|
||||
#define FG_OPTIMIZ_FULL_TIME 64000
|
||||
|
||||
struct ffc_smooth {
|
||||
int curr_lim;
|
||||
int time;
|
||||
};
|
||||
|
||||
static struct ffc_smooth ffc_dischg_smooth[FFC_SMOOTH_LEN] = {
|
||||
{ 0, 300000 },
|
||||
{ 300, 150000 },
|
||||
{ 600, 72000 },
|
||||
{ 1000, 50000 },
|
||||
};
|
||||
|
||||
int bq_battery_soc_smooth_tracking(struct bq_fg_chip *bq, int raw_soc,
|
||||
int batt_soc, int batt_temp, int batt_ma);
|
||||
|
||||
#endif /* __XM_SOC_SMOOTH_H */
|
115
drivers/power/supply/xiaomi/platform/external/fg/xm_battery_auth.c
vendored
Normal file
115
drivers/power/supply/xiaomi/platform/external/fg/xm_battery_auth.c
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
#include "inc/bq27z561.h"
|
||||
#include "inc/bq27z561_iio.h"
|
||||
#include "inc/xm_battery_auth.h"
|
||||
#include "inc/xm_soc_smooth.h"
|
||||
|
||||
int get_verify_digest(char *buf)
|
||||
{
|
||||
u8 digest_buf[4];
|
||||
int i;
|
||||
int len;
|
||||
|
||||
for (i = 0; i < BATTERY_DIGEST_LEN; i++) {
|
||||
memset(digest_buf, 0, sizeof(digest_buf));
|
||||
snprintf(digest_buf, sizeof(digest_buf) - 1, "%02x",
|
||||
g_bq27z561->digest[i]);
|
||||
strlcat(buf, digest_buf, BATTERY_DIGEST_LEN * 2 + 1);
|
||||
}
|
||||
len = strlen(buf);
|
||||
buf[len] = '\0';
|
||||
|
||||
pr_err("buf = %s\n", buf);
|
||||
return strlen(buf) + 1;
|
||||
}
|
||||
EXPORT_SYMBOL(get_verify_digest);
|
||||
|
||||
int set_verify_digest(u8 *rand_num)
|
||||
{
|
||||
int i;
|
||||
u8 random[32] = { 0 };
|
||||
char kbuf[70] = { 0 };
|
||||
|
||||
memset(kbuf, 0, sizeof(kbuf));
|
||||
strncpy(kbuf, rand_num, 64);
|
||||
|
||||
StringToHex(kbuf, random, &i);
|
||||
fg_sha256_auth(g_bq27z561, random, BATTERY_DIGEST_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(set_verify_digest);
|
||||
|
||||
int StringToHex(char *str, unsigned char *out, unsigned int *outlen)
|
||||
{
|
||||
char *p = str;
|
||||
char high = 0, low = 0;
|
||||
int tmplen = strlen(p), cnt = 0;
|
||||
tmplen = strlen(p);
|
||||
while (cnt < (tmplen / 2)) {
|
||||
high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*p - 48 - 7 :
|
||||
*p - 48;
|
||||
low = (*(++p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*(p)-48 - 7 :
|
||||
*(p)-48;
|
||||
out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));
|
||||
p++;
|
||||
cnt++;
|
||||
}
|
||||
if (tmplen % 2 != 0)
|
||||
out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*p - 48 - 7 :
|
||||
*p - 48;
|
||||
|
||||
if (outlen != NULL)
|
||||
*outlen = tmplen / 2 + tmplen % 2;
|
||||
|
||||
return tmplen / 2 + tmplen % 2;
|
||||
}
|
||||
|
||||
int fg_sha256_auth(struct bq_fg_chip *bq, u8 *rand_num, int length)
|
||||
{
|
||||
int ret;
|
||||
u8 cksum_calc;
|
||||
u8 t_buf[2];
|
||||
|
||||
/*
|
||||
1. The host writes 0x00 to 0x3E.
|
||||
2. The host writes 0x00 to 0x3F
|
||||
*/
|
||||
t_buf[0] = 0x00;
|
||||
t_buf[1] = 0x00;
|
||||
ret = fg_write_block(bq, bq->regs[BQ_FG_REG_ALT_MAC], t_buf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
3. Write the random challenge should be written in a 32-byte block to address 0x40-0x5F
|
||||
*/
|
||||
msleep(2);
|
||||
|
||||
ret = fg_write_block(bq, bq->regs[BQ_FG_REG_MAC_DATA], rand_num,
|
||||
length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*4. Write the checksum (2’s complement sum of (1), (2), and (3)) to address 0x60.*/
|
||||
cksum_calc = checksum(rand_num, length);
|
||||
ret = fg_write_byte(bq, bq->regs[BQ_FG_REG_MAC_CHKSUM], cksum_calc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*5. Write the length 0x24 to address 0x61.*/
|
||||
ret = fg_write_byte(bq, bq->regs[BQ_FG_REG_MAC_DATA_LEN], 0x24);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msleep(300);
|
||||
|
||||
ret = fg_read_block(bq, bq->regs[BQ_FG_REG_MAC_DATA], bq->digest,
|
||||
length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
145
drivers/power/supply/xiaomi/platform/external/fg/xm_soc_smooth.c
vendored
Normal file
145
drivers/power/supply/xiaomi/platform/external/fg/xm_soc_smooth.c
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
|
||||
#include "inc/bq27z561.h"
|
||||
#include "inc/bq27z561_iio.h"
|
||||
#include "inc/xm_battery_auth.h"
|
||||
#include "inc/xm_soc_smooth.h"
|
||||
|
||||
static int log_level = 2;
|
||||
#define smooth_err(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 0) \
|
||||
printk(KERN_ERR "[xm_soc_smooth] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define smooth_info(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 1) \
|
||||
printk(KERN_ERR "[xm_soc_smooth] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define smooth_dbg(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 2) \
|
||||
printk(KERN_ERR "[xm_soc_smooth] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#ifndef abs
|
||||
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||
#endif
|
||||
|
||||
/* get MonotonicSoc*/
|
||||
#define HW_REPORT_FULL_SOC 9700
|
||||
#define SOC_PROPORTION 98
|
||||
#define SOC_PROPORTION_C 97
|
||||
|
||||
int bq_battery_soc_smooth_tracking(struct bq_fg_chip *bq, int raw_soc, int soc,
|
||||
int temp, int batt_ma)
|
||||
{
|
||||
int status;
|
||||
static int system_soc, last_system_soc;
|
||||
int change_delta = 0;
|
||||
int soc_delta = 0, delta_time = 0, unit_time = 10000;
|
||||
static ktime_t last_change_time = -1;
|
||||
int soc_changed = 0;
|
||||
|
||||
ktime_t tmp_time = 0;
|
||||
struct timespec64 time;
|
||||
static int ibat_pos_count = 0;
|
||||
|
||||
tmp_time = ktime_get_boottime();
|
||||
time = ktime_to_timespec64(tmp_time);
|
||||
|
||||
if ((batt_ma > 0) && (ibat_pos_count < 10))
|
||||
ibat_pos_count++;
|
||||
else if (batt_ma <= 0)
|
||||
ibat_pos_count = 0;
|
||||
|
||||
status = fg_get_batt_status(bq);
|
||||
|
||||
// Map soc value according to raw_soc
|
||||
if (raw_soc > HW_REPORT_FULL_SOC)
|
||||
system_soc = 100;
|
||||
else {
|
||||
system_soc = ((raw_soc + SOC_PROPORTION_C) / SOC_PROPORTION);
|
||||
if (system_soc > 99)
|
||||
system_soc = 99;
|
||||
}
|
||||
|
||||
// Get the initial value for the first time
|
||||
if (last_change_time == -1) {
|
||||
last_change_time = ktime_get();
|
||||
if (system_soc != 0)
|
||||
last_system_soc = system_soc;
|
||||
else
|
||||
last_system_soc = soc;
|
||||
}
|
||||
|
||||
// If the soc jump, will smooth one cap every 10S
|
||||
soc_delta = abs(system_soc - last_system_soc);
|
||||
if (soc_delta > 1 || (bq->batt_volt < 3300 && system_soc > 0)) {
|
||||
calc_delta_time(last_change_time, &change_delta);
|
||||
delta_time = change_delta / unit_time;
|
||||
if (delta_time < 0) {
|
||||
last_change_time = ktime_get();
|
||||
delta_time = 0;
|
||||
}
|
||||
|
||||
smooth_err(
|
||||
"%s: system soc=%d,last system soc: %d,delta time: %d\n",
|
||||
__func__, system_soc, last_system_soc, delta_time);
|
||||
|
||||
soc_changed = min(1, delta_time);
|
||||
if (soc_changed) {
|
||||
if ((status == POWER_SUPPLY_STATUS_CHARGING) &&
|
||||
(system_soc > last_system_soc))
|
||||
system_soc = last_system_soc + soc_changed;
|
||||
else if (status == POWER_SUPPLY_STATUS_DISCHARGING &&
|
||||
system_soc < last_system_soc)
|
||||
system_soc = last_system_soc - soc_changed;
|
||||
} else
|
||||
system_soc = last_system_soc;
|
||||
|
||||
smooth_err("%s: fg jump smooth soc_changed=%d\n", __func__,
|
||||
soc_changed);
|
||||
}
|
||||
|
||||
if (system_soc < last_system_soc)
|
||||
system_soc = last_system_soc - 1;
|
||||
|
||||
// Avoid mismatches between charging status and soc changes
|
||||
if (((status == POWER_SUPPLY_STATUS_DISCHARGING) &&
|
||||
(system_soc > last_system_soc)) ||
|
||||
((status == POWER_SUPPLY_STATUS_CHARGING) &&
|
||||
(system_soc < last_system_soc) && (ibat_pos_count < 3) &&
|
||||
((time.tv_sec > 10))))
|
||||
system_soc = last_system_soc;
|
||||
|
||||
smooth_err(
|
||||
"%s: smooth_new:sys_soc:%d last_sys_soc:%d soc_delta:%d charging_status:%d unit_time:%d batt_ma_now=%d\n",
|
||||
__func__, system_soc, last_system_soc, soc_delta, status,
|
||||
unit_time, batt_ma);
|
||||
|
||||
if (system_soc != last_system_soc) {
|
||||
last_change_time = ktime_get();
|
||||
last_system_soc = system_soc;
|
||||
}
|
||||
|
||||
if (system_soc > 100)
|
||||
system_soc = 100;
|
||||
if (system_soc < 0)
|
||||
system_soc = 0;
|
||||
|
||||
if ((system_soc == 0) &&
|
||||
((bq->batt_volt >= 3400) || ((time.tv_sec <= 10)))) {
|
||||
system_soc = 1;
|
||||
smooth_err("%s: uisoc::hold 1 when volt > 3400mv. \n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (bq->last_soc != system_soc)
|
||||
bq->last_soc = system_soc;
|
||||
|
||||
return system_soc;
|
||||
}
|
4
drivers/power/supply/xiaomi/platform/external/mainchg/Makefile
vendored
Normal file
4
drivers/power/supply/xiaomi/platform/external/mainchg/Makefile
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CHARGER_SYV690D) += syv690d_drv.o
|
||||
syv690d_drv-objs := syv690d.o syv690d_iio.o
|
194
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d.h
vendored
Normal file
194
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d.h
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
|
||||
|
||||
#ifndef __SYV690D_H
|
||||
#define __SYV690D_H
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
#include <linux/battmngr/battmngr_voter.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#define PROBE_CNT_MAX 50
|
||||
|
||||
#define POWER_SUPPLY_TYPE_USB_FLOAT QTI_POWER_SUPPLY_TYPE_USB_FLOAT
|
||||
#define POWER_SUPPLY_TYPE_USB_HVDCP QTI_POWER_SUPPLY_TYPE_USB_HVDCP
|
||||
|
||||
enum bq2589x_vbus_type {
|
||||
BQ2589X_VBUS_NONE,
|
||||
BQ2589X_VBUS_USB_SDP,
|
||||
BQ2589X_VBUS_USB_CDP, /*CDP for bq25890, Adapter for bq25892*/
|
||||
BQ2589X_VBUS_USB_DCP,
|
||||
BQ2589X_VBUS_MAXC,
|
||||
BQ2589X_VBUS_UNKNOWN,
|
||||
BQ2589X_VBUS_NONSTAND,
|
||||
BQ2589X_VBUS_OTG,
|
||||
BQ2589X_VBUS_TYPE_NUM,
|
||||
};
|
||||
|
||||
enum bq2589x_part_no {
|
||||
SYV690 = 0x01,
|
||||
BQ25890 = 0x03,
|
||||
BQ25892 = 0x00,
|
||||
BQ25895 = 0x07,
|
||||
SC89890H = 0x04,
|
||||
};
|
||||
|
||||
#define BQ2589X_STATUS_PLUGIN 0x0001
|
||||
#define BQ2589X_STATUS_PG 0x0002
|
||||
#define BQ2589X_STATUS_CHARGE_ENABLE 0x0004
|
||||
#define BQ2589X_STATUS_FAULT 0x0008
|
||||
#define BQ2589X_STATUS_EXIST 0x0100
|
||||
|
||||
/* voters for syv690d */
|
||||
#define BQ_FCC_VOTER "BQ_FCC_VOTER"
|
||||
#define BQ_ICL_VOTER "BQ_ICL_VOTER"
|
||||
#define USER_VOTER "USER_VOTER"
|
||||
#define MAIN_CHG_ICL_VOTER "MAIN_CHG_ICL_VOTER"
|
||||
#define DETECT_FCC_VOTER "DETECT_FCC_VOTER"
|
||||
|
||||
#define BQ2589X_BAT_COMP 0
|
||||
#define BQ2589X_VCLAMP 0
|
||||
|
||||
extern struct bq2589x *g_bq2589x;
|
||||
|
||||
struct bq2589x_config {
|
||||
bool enable_auto_dpdm;
|
||||
/* bool enable_12v;*/
|
||||
|
||||
int battery_voltage_term;
|
||||
int charge_current;
|
||||
int input_current_limit;
|
||||
|
||||
bool enable_term;
|
||||
int term_current;
|
||||
int charge_voltage;
|
||||
|
||||
bool enable_ico;
|
||||
bool use_absolute_vindpm;
|
||||
bool otg_status;
|
||||
int otg_vol;
|
||||
int otg_current;
|
||||
};
|
||||
|
||||
struct bq2589x {
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
enum bq2589x_part_no part_no;
|
||||
int revision;
|
||||
|
||||
unsigned int status;
|
||||
int vbus_type;
|
||||
int charge_type;
|
||||
|
||||
bool enabled;
|
||||
|
||||
int vbus_volt;
|
||||
int vbat_volt;
|
||||
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct iio_channel *int_iio_chans;
|
||||
|
||||
int rsoc;
|
||||
struct bq2589x_config cfg;
|
||||
struct work_struct irq_work;
|
||||
struct work_struct adapter_in_work;
|
||||
struct work_struct adapter_out_work;
|
||||
struct delayed_work monitor_work;
|
||||
struct delayed_work ico_work;
|
||||
struct delayed_work force_work;
|
||||
struct delayed_work first_irq_work;
|
||||
struct delayed_work dump_regs_work;
|
||||
struct mutex bq2589x_i2c_lock;
|
||||
|
||||
struct wakeup_source *xm_ws;
|
||||
int wakeup_flag;
|
||||
|
||||
struct power_supply *batt_psy;
|
||||
struct power_supply *usb_psy;
|
||||
|
||||
struct votable *fcc_votable;
|
||||
struct votable *usb_icl_votable;
|
||||
struct votable *input_suspend_votable;
|
||||
|
||||
int old_type;
|
||||
int otg_gpio;
|
||||
int irq_gpio;
|
||||
int hz_flag;
|
||||
int curr_flag;
|
||||
bool force_exit_flag;
|
||||
int count;
|
||||
int input_suspend;
|
||||
struct regulator *dpdm_reg;
|
||||
struct mutex dpdm_lock;
|
||||
int dpdm_enabled;
|
||||
u8 vbus_state;
|
||||
u8 power_state;
|
||||
};
|
||||
|
||||
int bq2589x_update_bits(struct bq2589x *bq, u8 reg, u8 mask, u8 data);
|
||||
int sc89890h_set_hv(struct bq2589x *bq, u8 hv);
|
||||
int bq2589x_get_chg_type(struct bq2589x *bq);
|
||||
int bq2589x_get_chg_usb_type(struct bq2589x *bq);
|
||||
int bq2589x_enable_otg(struct bq2589x *bq);
|
||||
int bq2589x_disable_otg(struct bq2589x *bq);
|
||||
int bq2589x_set_otg_volt(struct bq2589x *bq, int volt);
|
||||
int bq2589x_set_otg_current(struct bq2589x *bq, int curr);
|
||||
int bq2589x_enable_charger(struct bq2589x *bq);
|
||||
int bq2589x_disable_charger(struct bq2589x *bq);
|
||||
int bq2589x_get_charger_enable(struct bq2589x *bq);
|
||||
int bq2589x_get_reserved_val(struct bq2589x *bq);
|
||||
int bq2589x_reserved_disable(struct bq2589x *bq);
|
||||
int bq2589x_adc_start(struct bq2589x *bq, bool oneshot);
|
||||
int bq2589x_adc_stop(struct bq2589x *bq);
|
||||
int bq2589x_adc_read_battery_volt(struct bq2589x *bq);
|
||||
int bq2589x_adc_read_vbus_volt(struct bq2589x *bq);
|
||||
int bq2589x_read_vindpm_volt(struct bq2589x *bq);
|
||||
int bq2589x_adc_read_charge_current(struct bq2589x *bq);
|
||||
int bq2589x_set_charge_current(struct bq2589x *bq, int curr);
|
||||
int bq2589x_set_term_current(struct bq2589x *bq, int curr);
|
||||
int bq2589x_set_prechg_current(struct bq2589x *bq, int curr);
|
||||
int bq2589x_set_chargevoltage(struct bq2589x *bq, int volt);
|
||||
int bq2589x_set_input_volt_limit(struct bq2589x *bq, int volt);
|
||||
int bq2589x_set_input_current_limit(struct bq2589x *bq, int curr);
|
||||
int bq2589x_set_vindpm_offset(struct bq2589x *bq, int offset);
|
||||
int bq2589x_get_charging_status(struct bq2589x *bq);
|
||||
int bq2589x_disable_watchdog_timer(struct bq2589x *bq);
|
||||
int bq2589x_set_watchdog_timer(struct bq2589x *bq, u8 timeout);
|
||||
int bq2589x_reset_watchdog_timer(struct bq2589x *bq);
|
||||
int bq2589x_is_dpdm_done(struct bq2589x *bq, int *done);
|
||||
int bq2589x_force_dpdm(struct bq2589x *bq);
|
||||
void bq2589x_force_dpdm_done(struct bq2589x *bq);
|
||||
int bq2589x_reset_chip(struct bq2589x *bq);
|
||||
int bq2589x_enter_hiz_mode(struct bq2589x *bq);
|
||||
int bq2589x_exit_hiz_mode(struct bq2589x *bq);
|
||||
int bq2589x_get_hiz_mode(struct bq2589x *bq, u8 *state);
|
||||
int bq2589x_force_ico(struct bq2589x *bq);
|
||||
int bq2589x_check_force_ico_done(struct bq2589x *bq);
|
||||
int bq2589x_enable_term(struct bq2589x *bq, bool enable);
|
||||
int bq2589x_enable_auto_dpdm(struct bq2589x *bq, bool enable);
|
||||
int bq2589x_use_absolute_vindpm(struct bq2589x *bq, bool enable);
|
||||
int bq2589x_enable_ico(struct bq2589x *bq, bool enable);
|
||||
bool bq2589x_is_charge_present(struct bq2589x *bq);
|
||||
bool bq2589x_is_charge_online(struct bq2589x *bq);
|
||||
bool bq2589x_is_charge_done(struct bq2589x *bq);
|
||||
void bq2589x_dump_regs(struct bq2589x *bq);
|
||||
int bq2589x_init_device(struct bq2589x *bq);
|
||||
int bq2589x_charge_status(struct bq2589x *bq);
|
||||
int bq2589x_detect_device(struct bq2589x *bq);
|
||||
int charger_request_dpdm(struct bq2589x *charger, int enable);
|
||||
int bq_init_iio_psy(struct bq2589x *chip);
|
||||
|
||||
#endif /* __SYV690D_H */
|
68
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d_iio.h
vendored
Normal file
68
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d_iio.h
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SYV690D_IIO_H
|
||||
#define __SYV690D_IIO_H
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/qti_power_supply.h>
|
||||
#include <dt-bindings/iio/qti_power_supply_iio.h>
|
||||
|
||||
struct syv690d_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define BQ25890_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define BQ25890_CHAN_ENERGY(_name, _num) \
|
||||
BQ25890_IIO_CHAN(_name, _num, IIO_ENERGY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
static const struct syv690d_iio_channels syv690d_iio_psy_channels[] = {
|
||||
BQ25890_CHAN_ENERGY("syv_charge_present", PSY_IIO_SYV_CHARGE_PRESENT) BQ25890_CHAN_ENERGY(
|
||||
"syv_charge_online",
|
||||
PSY_IIO_SYV_CHARGE_ONLINE) BQ25890_CHAN_ENERGY("syv_charge_done",
|
||||
PSY_IIO_SYV_CHARGE_DONE)
|
||||
BQ25890_CHAN_ENERGY("syv_chager_hz", PSY_IIO_SYV_CHAGER_HZ) BQ25890_CHAN_ENERGY(
|
||||
"syv_input_current_settled",
|
||||
PSY_IIO_SYV_INPUT_CURRENT_SETTLED) BQ25890_CHAN_ENERGY("syv_input_voltage_settled",
|
||||
PSY_IIO_SYV_INPUT_VOLTAGE_SETTLED)
|
||||
BQ25890_CHAN_ENERGY("syv_charge_current", PSY_IIO_SYV_CHAGER_CURRENT) BQ25890_CHAN_ENERGY(
|
||||
"syv_charger_enable",
|
||||
PSY_IIO_SYV_CHARGING_ENABLED) BQ25890_CHAN_ENERGY("syv_otg_enable",
|
||||
PSY_IIO_SYV_OTG_ENABLE)
|
||||
BQ25890_CHAN_ENERGY("syv_charger_term", PSY_IIO_SYV_CHAGER_TERM) BQ25890_CHAN_ENERGY(
|
||||
"syv_batt_voltage_term",
|
||||
PSY_IIO_SYV_BATTERY_VOLTAGE_TERM)
|
||||
BQ25890_CHAN_ENERGY("syv_charger_status", PSY_IIO_SYV_CHARGER_STATUS) BQ25890_CHAN_ENERGY(
|
||||
"syv_charger_type",
|
||||
PSY_IIO_SYV_CHARGE_TYPE)
|
||||
BQ25890_CHAN_ENERGY(
|
||||
"syv_charger_usb_type",
|
||||
PSY_IIO_SYV_CHARGE_USB_TYPE)
|
||||
BQ25890_CHAN_ENERGY(
|
||||
"syv_vbus_voltage",
|
||||
PSY_IIO_SYV_BUS_VOLTAGE)
|
||||
BQ25890_CHAN_ENERGY(
|
||||
"syv_vbat_voltage",
|
||||
PSY_IIO_SYV_BATTERY_VOLTAGE)
|
||||
BQ25890_CHAN_ENERGY(
|
||||
"syv_enable_charger_term",
|
||||
PSY_IIO_SYV_ENABLE_CHAGER_TERM)
|
||||
};
|
||||
|
||||
int bq_init_iio_psy(struct bq2589x *chip);
|
||||
|
||||
#endif /* __SYV690D_IIO_H */
|
377
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d_reg.h
vendored
Normal file
377
drivers/power/supply/xiaomi/platform/external/mainchg/inc/syv690d_reg.h
vendored
Normal file
@ -0,0 +1,377 @@
|
||||
|
||||
#ifndef __SYV690D_REG_H
|
||||
#define __SYV690D_REG_H
|
||||
|
||||
/* Register 00h */
|
||||
#define BQ2589X_REG_00 0x00
|
||||
#define BQ2589X_ENHIZ_MASK 0x80
|
||||
#define BQ2589X_ENHIZ_SHIFT 7
|
||||
#define BQ2589X_HIZ_ENABLE 1
|
||||
#define BQ2589X_HIZ_DISABLE 0
|
||||
#define BQ2589X_ENILIM_MASK 0x40
|
||||
#define BQ2589X_ENILIM_SHIFT 6
|
||||
#define BQ2589X_ENILIM_ENABLE 1
|
||||
#define BQ2589X_ENILIM_DISABLE 0
|
||||
|
||||
#define BQ2589X_IINLIM_MASK 0x3F
|
||||
#define BQ2589X_IINLIM_SHIFT 0
|
||||
#define BQ2589X_IINLIM_BASE 100
|
||||
#define BQ2589X_IINLIM_LSB 50
|
||||
|
||||
/* Register 01h */
|
||||
#define BQ2589X_REG_01 0x01
|
||||
#define BQ2589X_BHOT_MASK 0xC0
|
||||
#define BQ2589X_BHOT_SHIFT 6
|
||||
#define BQ2589X_BCOLD_MASK 0x20
|
||||
#define BQ2589X_BCOLD_SHIFT 5
|
||||
#define BQ2589X_VINDPMOS_MASK 0x1F
|
||||
#define BQ2589X_VINDPMOS_SHIFT 0
|
||||
|
||||
#define BQ2589X_VINDPMOS_BASE 0
|
||||
#define BQ2589X_VINDPMOS_LSB 100
|
||||
|
||||
/* Register 0x02 */
|
||||
#define BQ2589X_REG_02 0x02
|
||||
#define BQ2589X_CONV_START_MASK 0x80
|
||||
#define BQ2589X_CONV_START_SHIFT 7
|
||||
#define BQ2589X_CONV_START 0
|
||||
#define BQ2589X_CONV_STOP 1
|
||||
#define BQ2589X_CONV_RATE_MASK 0x40
|
||||
#define BQ2589X_CONV_RATE_SHIFT 6
|
||||
#define BQ2589X_ADC_CONTINUE_ENABLE 1
|
||||
#define BQ2589X_ADC_CONTINUE_DISABLE 0
|
||||
|
||||
#define BQ2589X_BOOST_FREQ_MASK 0x20
|
||||
#define BQ2589X_BOOST_FREQ_SHIFT 5
|
||||
#define BQ2589X_BOOST_FREQ_1500K 0
|
||||
#define BQ2589X_BOOST_FREQ_500K 0
|
||||
|
||||
#define BQ2589X_ICOEN_MASK 0x10
|
||||
#define BQ2589X_ICOEN_SHIFT 4
|
||||
#define BQ2589X_ICO_ENABLE 1
|
||||
#define BQ2589X_ICO_DISABLE 0
|
||||
#define BQ2589X_HVDCPEN_MASK 0x08
|
||||
#define BQ2589X_HVDCPEN_SHIFT 3
|
||||
#define BQ2589X_HVDCP_ENABLE 1
|
||||
#define BQ2589X_HVDCP_DISABLE 0
|
||||
#define BQ2589X_MAXCEN_MASK 0x04
|
||||
#define BQ2589X_MAXCEN_SHIFT 2
|
||||
#define BQ2589X_MAXC_ENABLE 1
|
||||
#define BQ2589X_MAXC_DISABLE 0
|
||||
|
||||
#define BQ2589X_FORCE_DPDM_MASK 0x02
|
||||
#define BQ2589X_FORCE_DPDM_SHIFT 1
|
||||
#define BQ2589X_FORCE_DPDM 1
|
||||
#define BQ2589X_AUTO_DPDM_EN_MASK 0x01
|
||||
#define BQ2589X_AUTO_DPDM_EN_SHIFT 0
|
||||
#define BQ2589X_AUTO_DPDM_ENABLE 1
|
||||
#define BQ2589X_AUTO_DPDM_DISABLE 0
|
||||
|
||||
/* Register 0x03 */
|
||||
#define BQ2589X_REG_03 0x03
|
||||
#define BQ2589X_BAT_LOADEN_MASK 0x80
|
||||
#define BQ2589X_BAT_LOAEN_SHIFT 7
|
||||
#define BQ2589X_WDT_RESET_MASK 0x40
|
||||
#define BQ2589X_WDT_RESET_SHIFT 6
|
||||
#define BQ2589X_WDT_RESET 1
|
||||
|
||||
#define BQ2589X_OTG_CONFIG_MASK 0x20
|
||||
#define BQ2589X_OTG_CONFIG_SHIFT 5
|
||||
#define BQ2589X_OTG_ENABLE 1
|
||||
#define BQ2589X_OTG_DISABLE 0
|
||||
|
||||
#define BQ2589X_CHG_CONFIG_MASK 0x10
|
||||
#define BQ2589X_CHG_CONFIG_SHIFT 4
|
||||
#define BQ2589X_CHG_ENABLE 1
|
||||
#define BQ2589X_CHG_DISABLE 0
|
||||
|
||||
#define BQ2589X_SYS_MINV_MASK 0x0E
|
||||
#define BQ2589X_SYS_MINV_SHIFT 1
|
||||
|
||||
#define BQ2589X_SYS_MINV_BASE 3000
|
||||
#define BQ2589X_SYS_MINV_LSB 100
|
||||
|
||||
#define BQ2589X_RESERVED_MASK 0x01
|
||||
#define BQ2589X_RESERVED_SHIFT 0
|
||||
#define BQ2589X_RESERVED_ENABLE 1
|
||||
#define BQ2589X_RESERVED_DISABLE 0
|
||||
|
||||
/* Register 0x04*/
|
||||
#define BQ2589X_REG_04 0x04
|
||||
#define BQ2589X_EN_PUMPX_MASK 0x80
|
||||
#define BQ2589X_EN_PUMPX_SHIFT 7
|
||||
#define BQ2589X_PUMPX_ENABLE 1
|
||||
#define BQ2589X_PUMPX_DISABLE 0
|
||||
#define BQ2589X_ICHG_MASK 0x7F
|
||||
#define BQ2589X_ICHG_SHIFT 0
|
||||
#define BQ2589X_ICHG_BASE 0
|
||||
#define BQ2589X_ICHG_LSB 64
|
||||
|
||||
/* Register 0x05*/
|
||||
#define BQ2589X_REG_05 0x05
|
||||
#define BQ2589X_IPRECHG_MASK 0xF0
|
||||
#define BQ2589X_IPRECHG_SHIFT 4
|
||||
#define BQ2589X_ITERM_MASK 0x0F
|
||||
#define BQ2589X_ITERM_SHIFT 0
|
||||
#define BQ2589X_IPRECHG_BASE 64
|
||||
#define BQ2589X_IPRECHG_LSB 64
|
||||
#define BQ2589X_ITERM_BASE 64
|
||||
#define BQ2589X_ITERM_LSB 64
|
||||
|
||||
/* Register 0x06*/
|
||||
#define BQ2589X_REG_06 0x06
|
||||
#define BQ2589X_VREG_MASK 0xFC
|
||||
#define BQ2589X_VREG_SHIFT 2
|
||||
#define BQ2589X_BATLOWV_MASK 0x02
|
||||
#define BQ2589X_BATLOWV_SHIFT 1
|
||||
#define BQ2589X_BATLOWV_2800MV 0
|
||||
#define BQ2589X_BATLOWV_3000MV 1
|
||||
#define BQ2589X_VRECHG_MASK 0x01
|
||||
#define BQ2589X_VRECHG_SHIFT 0
|
||||
#define BQ2589X_VRECHG_100MV 0
|
||||
#define BQ2589X_VRECHG_200MV 1
|
||||
#define BQ2589X_VREG_BASE 3840
|
||||
#define BQ2589X_VREG_LSB 16
|
||||
|
||||
/* Register 0x07*/
|
||||
#define BQ2589X_REG_07 0x07
|
||||
#define BQ2589X_EN_TERM_MASK 0x80
|
||||
#define BQ2589X_EN_TERM_SHIFT 7
|
||||
#define BQ2589X_TERM_ENABLE 1
|
||||
#define BQ2589X_TERM_DISABLE 0
|
||||
|
||||
#define BQ2589X_WDT_MASK 0x30
|
||||
#define BQ2589X_WDT_SHIFT 4
|
||||
#define BQ2589X_WDT_DISABLE 0
|
||||
#define BQ2589X_WDT_40S 1
|
||||
#define BQ2589X_WDT_80S 2
|
||||
#define BQ2589X_WDT_160S 3
|
||||
#define BQ2589X_WDT_BASE 0
|
||||
#define BQ2589X_WDT_LSB 40
|
||||
|
||||
#define BQ2589X_EN_TIMER_MASK 0x08
|
||||
#define BQ2589X_EN_TIMER_SHIFT 3
|
||||
|
||||
#define BQ2589X_CHG_TIMER_ENABLE 1
|
||||
#define BQ2589X_CHG_TIMER_DISABLE 0
|
||||
|
||||
#define BQ2589X_CHG_TIMER_MASK 0x06
|
||||
#define BQ2589X_CHG_TIMER_SHIFT 1
|
||||
#define BQ2589X_CHG_TIMER_5HOURS 0
|
||||
#define BQ2589X_CHG_TIMER_8HOURS 1
|
||||
#define BQ2589X_CHG_TIMER_12HOURS 2
|
||||
#define BQ2589X_CHG_TIMER_20HOURS 3
|
||||
|
||||
#define BQ2589X_JEITA_ISET_MASK 0x01
|
||||
#define BQ2589X_JEITA_ISET_SHIFT 0
|
||||
#define BQ2589X_JEITA_ISET_50PCT 0
|
||||
#define BQ2589X_JEITA_ISET_20PCT 1
|
||||
|
||||
/* Register 0x08*/
|
||||
#define BQ2589X_REG_08 0x08
|
||||
#define BQ2589X_BAT_COMP_MASK 0xE0
|
||||
#define BQ2589X_BAT_COMP_SHIFT 5
|
||||
#define BQ2589X_VCLAMP_MASK 0x1C
|
||||
#define BQ2589X_VCLAMP_SHIFT 2
|
||||
#define BQ2589X_TREG_MASK 0x03
|
||||
#define BQ2589X_TREG_SHIFT 0
|
||||
#define BQ2589X_TREG_60C 0
|
||||
#define BQ2589X_TREG_80C 1
|
||||
#define BQ2589X_TREG_100C 2
|
||||
#define BQ2589X_TREG_120C 3
|
||||
|
||||
#define BQ2589X_BAT_COMP_BASE 0
|
||||
#define BQ2589X_BAT_COMP_LSB 20
|
||||
#define BQ2589X_VCLAMP_BASE 0
|
||||
#define BQ2589X_VCLAMP_LSB 32
|
||||
|
||||
/* Register 0x09*/
|
||||
#define BQ2589X_REG_09 0x09
|
||||
#define BQ2589X_FORCE_ICO_MASK 0x80
|
||||
#define BQ2589X_FORCE_ICO_SHIFT 7
|
||||
#define BQ2589X_FORCE_ICO 1
|
||||
#define BQ2589X_TMR2X_EN_MASK 0x40
|
||||
#define BQ2589X_TMR2X_EN_SHIFT 6
|
||||
#define BQ2589X_BATFET_DIS_MASK 0x20
|
||||
#define BQ2589X_BATFET_DIS_SHIFT 5
|
||||
#define BQ2589X_BATFET_OFF 1
|
||||
|
||||
#define BQ2589X_JEITA_VSET_MASK 0x10
|
||||
#define BQ2589X_JEITA_VSET_SHIFT 4
|
||||
#define BQ2589X_JEITA_VSET_N150MV 0
|
||||
#define BQ2589X_JEITA_VSET_VREG 1
|
||||
#define BQ2589X_BATFET_RST_EN_MASK 0x04
|
||||
#define BQ2589X_BATFET_RST_EN_SHIFT 2
|
||||
#define BQ2589X_PUMPX_UP_MASK 0x02
|
||||
#define BQ2589X_PUMPX_UP_SHIFT 1
|
||||
#define BQ2589X_PUMPX_UP 1
|
||||
#define BQ2589X_PUMPX_DOWN_MASK 0x01
|
||||
#define BQ2589X_PUMPX_DOWN_SHIFT 0
|
||||
#define BQ2589X_PUMPX_DOWN 1
|
||||
|
||||
/* Register 0x0A*/
|
||||
#define BQ2589X_REG_0A 0x0A
|
||||
#define BQ2589X_BOOSTV_MASK 0xF0
|
||||
#define BQ2589X_BOOSTV_SHIFT 4
|
||||
#define BQ2589X_BOOSTV_BASE 4550
|
||||
#define BQ2589X_BOOSTV_LSB 64
|
||||
|
||||
#define BQ2589X_BOOST_LIM_MASK 0x07
|
||||
#define BQ2589X_BOOST_LIM_SHIFT 0
|
||||
#define BQ2589X_BOOST_LIM_500MA 0x00
|
||||
#define BQ2589X_BOOST_LIM_700MA 0x01
|
||||
#define BQ2589X_BOOST_LIM_1100MA 0x02
|
||||
#define BQ2589X_BOOST_LIM_1300MA 0x03
|
||||
#define BQ2589X_BOOST_LIM_1600MA 0x04
|
||||
#define BQ2589X_BOOST_LIM_1800MA 0x05
|
||||
#define BQ2589X_BOOST_LIM_2100MA 0x06
|
||||
#define BQ2589X_BOOST_LIM_2400MA 0x07
|
||||
|
||||
/* Register 0x0B*/
|
||||
#define BQ2589X_REG_0B 0x0B
|
||||
#define BQ2589X_VBUS_STAT_MASK 0xE0
|
||||
#define BQ2589X_VBUS_STAT_SHIFT 5
|
||||
#define BQ2589X_CHRG_STAT_MASK 0x18
|
||||
#define BQ2589X_CHRG_STAT_SHIFT 3
|
||||
#define BQ2589X_CHRG_STAT_IDLE 0
|
||||
#define BQ2589X_CHRG_STAT_PRECHG 1
|
||||
#define BQ2589X_CHRG_STAT_FASTCHG 2
|
||||
#define BQ2589X_CHRG_STAT_CHGDONE 3
|
||||
|
||||
#define BQ2589X_PG_STAT_MASK 0x04
|
||||
#define BQ2589X_PG_STAT_SHIFT 2
|
||||
#define BQ2589X_SDP_STAT_MASK 0x02
|
||||
#define BQ2589X_SDP_STAT_SHIFT 1
|
||||
#define BQ2589X_VSYS_STAT_MASK 0x01
|
||||
#define BQ2589X_VSYS_STAT_SHIFT 0
|
||||
|
||||
/* Register 0x0C*/
|
||||
#define BQ2589X_REG_0C 0x0c
|
||||
#define BQ2589X_FAULT_WDT_MASK 0x80
|
||||
#define BQ2589X_FAULT_WDT_SHIFT 7
|
||||
#define BQ2589X_FAULT_BOOST_MASK 0x40
|
||||
#define BQ2589X_FAULT_BOOST_SHIFT 6
|
||||
#define BQ2589X_FAULT_CHRG_MASK 0x30
|
||||
#define BQ2589X_FAULT_CHRG_SHIFT 4
|
||||
#define BQ2589X_FAULT_CHRG_NORMAL 0
|
||||
#define BQ2589X_FAULT_CHRG_INPUT 1
|
||||
#define BQ2589X_FAULT_CHRG_THERMAL 2
|
||||
#define BQ2589X_FAULT_CHRG_TIMER 3
|
||||
|
||||
#define BQ2589X_FAULT_BAT_MASK 0x08
|
||||
#define BQ2589X_FAULT_BAT_SHIFT 3
|
||||
#define BQ2589X_FAULT_NTC_MASK 0x07
|
||||
#define BQ2589X_FAULT_NTC_SHIFT 0
|
||||
#define BQ2589X_FAULT_NTC_TSCOLD 1
|
||||
#define BQ2589X_FAULT_NTC_TSHOT 2
|
||||
|
||||
#define BQ2589X_FAULT_NTC_WARM 2
|
||||
#define BQ2589X_FAULT_NTC_COOL 3
|
||||
#define BQ2589X_FAULT_NTC_COLD 5
|
||||
#define BQ2589X_FAULT_NTC_HOT 6
|
||||
|
||||
/* Register 0x0D*/
|
||||
#define BQ2589X_REG_0D 0x0D
|
||||
#define BQ2589X_FORCE_VINDPM_MASK 0x80
|
||||
#define BQ2589X_FORCE_VINDPM_SHIFT 7
|
||||
#define BQ2589X_FORCE_VINDPM_ENABLE 1
|
||||
#define BQ2589X_FORCE_VINDPM_DISABLE 0
|
||||
#define BQ2589X_VINDPM_MASK 0x7F
|
||||
#define BQ2589X_VINDPM_SHIFT 0
|
||||
|
||||
#define BQ2589X_VINDPM_BASE 2600
|
||||
#define BQ2589X_VINDPM_LSB 100
|
||||
|
||||
/* Register 0x0E*/
|
||||
#define BQ2589X_REG_0E 0x0E
|
||||
#define BQ2589X_THERM_STAT_MASK 0x80
|
||||
#define BQ2589X_THERM_STAT_SHIFT 7
|
||||
#define BQ2589X_BATV_MASK 0x7F
|
||||
#define BQ2589X_BATV_SHIFT 0
|
||||
#define BQ2589X_BATV_BASE 2304
|
||||
#define BQ2589X_BATV_LSB 20
|
||||
|
||||
/* Register 0x0F*/
|
||||
#define BQ2589X_REG_0F 0x0F
|
||||
#define BQ2589X_SYSV_MASK 0x7F
|
||||
#define BQ2589X_SYSV_SHIFT 0
|
||||
#define BQ2589X_SYSV_BASE 2304
|
||||
#define BQ2589X_SYSV_LSB 20
|
||||
|
||||
/* Register 0x10*/
|
||||
#define BQ2589X_REG_10 0x10
|
||||
#define BQ2589X_TSPCT_MASK 0x7F
|
||||
#define BQ2589X_TSPCT_SHIFT 0
|
||||
#define BQ2589X_TSPCT_BASE 21
|
||||
#define BQ2589X_TSPCT_LSB 465 //should be 0.465,kernel does not support float
|
||||
|
||||
/* Register 0x11*/
|
||||
#define BQ2589X_REG_11 0x11
|
||||
#define BQ2589X_VBUS_GD_MASK 0x80
|
||||
#define BQ2589X_VBUS_GD_SHIFT 7
|
||||
#define BQ2589X_VBUSV_MASK 0x7F
|
||||
#define BQ2589X_VBUSV_SHIFT 0
|
||||
#define BQ2589X_VBUSV_BASE 2600
|
||||
#define BQ2589X_VBUSV_LSB 100
|
||||
|
||||
/* Register 0x12*/
|
||||
#define BQ2589X_REG_12 0x12
|
||||
#define BQ2589X_ICHGR_MASK 0x7F
|
||||
#define BQ2589X_ICHGR_SHIFT 0
|
||||
#define BQ2589X_ICHGR_BASE 0
|
||||
#define BQ2589X_ICHGR_LSB 50
|
||||
|
||||
/* Register 0x13*/
|
||||
#define BQ2589X_REG_13 0x13
|
||||
#define BQ2589X_VDPM_STAT_MASK 0x80
|
||||
#define BQ2589X_VDPM_STAT_SHIFT 7
|
||||
#define BQ2589X_IDPM_STAT_MASK 0x40
|
||||
#define BQ2589X_IDPM_STAT_SHIFT 6
|
||||
#define BQ2589X_IDPM_LIM_MASK 0x3F
|
||||
#define BQ2589X_IDPM_LIM_SHIFT 0
|
||||
#define BQ2589X_IDPM_LIM_BASE 100
|
||||
#define BQ2589X_IDPM_LIM_LSB 50
|
||||
|
||||
/* Register 0x14*/
|
||||
#define BQ2589X_REG_14 0x14
|
||||
#define BQ2589X_RESET_MASK 0x80
|
||||
#define BQ2589X_RESET_SHIFT 7
|
||||
#define BQ2589X_RESET 1
|
||||
#define BQ2589X_ICO_OPTIMIZED_MASK 0x40
|
||||
#define BQ2589X_ICO_OPTIMIZED_SHIFT 6
|
||||
#define BQ2589X_PN_MASK 0x38
|
||||
#define BQ2589X_PN_SHIFT 3
|
||||
#define BQ2589X_TS_PROFILE_MASK 0x04
|
||||
#define BQ2589X_TS_PROFILE_SHIFT 2
|
||||
#define BQ2589X_DEV_REV_MASK 0x03
|
||||
#define BQ2589X_DEV_REV_SHIFT 0
|
||||
|
||||
/* SC89890H */
|
||||
/*0x01*/
|
||||
#define SC89890H_HV_5V 0x45
|
||||
#define SC89890H_HV_9V 0xC9
|
||||
#define SC89890H_HV_12V 0x49
|
||||
|
||||
#define SC89890H_FORCE_DPDM1 0x45
|
||||
#define SC89890H_FORCE_DPDM2 0x25
|
||||
|
||||
#define SC89890H_VINDPMOS_MASK 0x01
|
||||
#define SC89890H_VINDPMOS_SHIFT 0
|
||||
#define SC89890H_VINDPMOS_400MV 0
|
||||
#define SC89890H_VINDPMOS_600MV 1
|
||||
|
||||
/*0x04*/
|
||||
#define SC89890H_ICHG_LSB 60
|
||||
|
||||
/*0x05*/
|
||||
#define SC89890H_IPRECHG_BASE 60
|
||||
#define SC89890H_IPRECHG_LSB 60
|
||||
#define SC89890H_ITERM_BASE 30
|
||||
#define SC89890H_ITERM_LSB 60
|
||||
#define SC89890H_ITERM_MAX 930
|
||||
|
||||
/*0x0A*/
|
||||
#define SC89890H_BOOSTV_BASE 3900
|
||||
#define SC89890H_BOOSTV_LSB 100
|
||||
|
||||
#endif /* __SYV690D_REG_H */
|
2001
drivers/power/supply/xiaomi/platform/external/mainchg/syv690d.c
vendored
Normal file
2001
drivers/power/supply/xiaomi/platform/external/mainchg/syv690d.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
268
drivers/power/supply/xiaomi/platform/external/mainchg/syv690d_iio.c
vendored
Normal file
268
drivers/power/supply/xiaomi/platform/external/mainchg/syv690d_iio.c
vendored
Normal file
@ -0,0 +1,268 @@
|
||||
|
||||
#include "inc/syv690d.h"
|
||||
#include "inc/syv690d_reg.h"
|
||||
#include "inc/syv690d_iio.h"
|
||||
|
||||
static int bq_iio_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct bq2589x *bq = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
u8 state;
|
||||
*val1 = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SYV_CHARGE_PRESENT:
|
||||
*val1 = bq2589x_is_charge_present(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGE_ONLINE:
|
||||
*val1 = bq2589x_is_charge_online(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGE_DONE:
|
||||
*val1 = bq2589x_is_charge_done(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHAGER_HZ:
|
||||
rc = bq2589x_get_hiz_mode(bq, &state);
|
||||
if (!rc)
|
||||
*val1 = (int)state;
|
||||
break;
|
||||
case PSY_IIO_SYV_INPUT_CURRENT_SETTLED:
|
||||
*val1 = bq->cfg.input_current_limit;
|
||||
break;
|
||||
case PSY_IIO_SYV_INPUT_VOLTAGE_SETTLED:
|
||||
*val1 = bq2589x_read_vindpm_volt(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHAGER_CURRENT:
|
||||
*val1 = bq2589x_adc_read_charge_current(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGING_ENABLED:
|
||||
*val1 = bq2589x_get_charger_enable(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_BUS_VOLTAGE:
|
||||
*val1 = bq2589x_adc_read_vbus_volt(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_BATTERY_VOLTAGE:
|
||||
*val1 = bq2589x_adc_read_battery_volt(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_OTG_ENABLE:
|
||||
*val1 = bq->cfg.otg_status;
|
||||
break;
|
||||
case PSY_IIO_SYV_CHAGER_TERM:
|
||||
*val1 = bq->cfg.term_current;
|
||||
break;
|
||||
case PSY_IIO_SYV_BATTERY_VOLTAGE_TERM:
|
||||
*val1 = bq->cfg.battery_voltage_term;
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGER_STATUS:
|
||||
*val1 = bq2589x_charge_status(bq);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGE_TYPE:
|
||||
*val1 = bq2589x_get_chg_type(bq);
|
||||
;
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGE_USB_TYPE:
|
||||
*val1 = bq2589x_get_chg_usb_type(bq);
|
||||
;
|
||||
break;
|
||||
case PSY_IIO_SYV_ENABLE_CHAGER_TERM:
|
||||
*val1 = bq->cfg.enable_term;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported QG IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
pr_err_ratelimited("Couldn't read IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int bq_iio_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct bq2589x *bq = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
union power_supply_propval val = {
|
||||
0,
|
||||
};
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_SYV_CHAGER_HZ:
|
||||
if (val1) {
|
||||
bq2589x_enter_hiz_mode(bq);
|
||||
bq->hz_flag = true;
|
||||
} else {
|
||||
bq2589x_exit_hiz_mode(bq);
|
||||
bq->hz_flag = false;
|
||||
}
|
||||
power_supply_changed(bq->batt_psy);
|
||||
power_supply_changed(bq->usb_psy);
|
||||
pr_err("iio_write: hz_flag %d\n", bq->hz_flag);
|
||||
break;
|
||||
case PSY_IIO_SYV_INPUT_CURRENT_SETTLED:
|
||||
if (val1 >= 3100)
|
||||
val1 = 3100;
|
||||
bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENILIM_MASK,
|
||||
BQ2589X_ENILIM_DISABLE
|
||||
<< BQ2589X_ENILIM_SHIFT);
|
||||
bq2589x_set_input_current_limit(bq, val1);
|
||||
bq->cfg.input_current_limit = val1;
|
||||
break;
|
||||
case PSY_IIO_SYV_INPUT_VOLTAGE_SETTLED:
|
||||
bq2589x_use_absolute_vindpm(bq, true);
|
||||
bq2589x_set_input_volt_limit(bq, val1);
|
||||
pr_err("bq2589x_set_input_volt_limit %d\n", val1);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHAGER_CURRENT:
|
||||
if (val1 >= 3100)
|
||||
val1 = 3100;
|
||||
bq->cfg.charge_current = val1;
|
||||
bq2589x_set_charge_current(bq, val1);
|
||||
break;
|
||||
case PSY_IIO_SYV_CHARGING_ENABLED:
|
||||
pr_err("%s: PSY_IIO_SYV_CHARGING_ENABLED, val = %d\n", __func__,
|
||||
val1);
|
||||
if (val1)
|
||||
bq2589x_enable_charger(bq);
|
||||
else
|
||||
bq2589x_disable_charger(bq);
|
||||
power_supply_changed(bq->batt_psy);
|
||||
power_supply_changed(bq->usb_psy);
|
||||
break;
|
||||
case PSY_IIO_SYV_OTG_ENABLE:
|
||||
if (!g_bq2589x) {
|
||||
pr_err("%s: g_bq2589x is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
gpio_set_value(g_bq2589x->otg_gpio, val1);
|
||||
pr_err("%s: PSY_IIO_SYV_OTG_ENABLE, val = %d\n", __func__,
|
||||
val1);
|
||||
if (val1) {
|
||||
bq->cfg.otg_status = true;
|
||||
bq2589x_exit_hiz_mode(bq);
|
||||
bq2589x_disable_charger(bq);
|
||||
bq2589x_enable_otg(bq);
|
||||
bq2589x_set_otg_volt(bq, 5300);
|
||||
if (bq->batt_psy)
|
||||
power_supply_get_property(
|
||||
bq->batt_psy,
|
||||
POWER_SUPPLY_PROP_CAPACITY, &val);
|
||||
if (val.intval > 5)
|
||||
bq2589x_set_otg_current(bq, 1800);
|
||||
else
|
||||
bq2589x_set_otg_current(bq, 1500);
|
||||
if (!bq->wakeup_flag) {
|
||||
__pm_stay_awake(bq->xm_ws);
|
||||
bq->wakeup_flag = 1;
|
||||
pr_err("otg workup\n");
|
||||
}
|
||||
} else {
|
||||
bq->cfg.otg_status = false;
|
||||
bq2589x_disable_otg(bq);
|
||||
bq2589x_enable_charger(bq);
|
||||
if (bq->wakeup_flag) {
|
||||
__pm_relax(bq->xm_ws);
|
||||
bq->wakeup_flag = 0;
|
||||
pr_err("xm otg relax\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PSY_IIO_SYV_CHAGER_TERM:
|
||||
bq->cfg.term_current = val1;
|
||||
bq2589x_set_term_current(bq, val1);
|
||||
break;
|
||||
case PSY_IIO_SYV_BATTERY_VOLTAGE_TERM:
|
||||
bq2589x_set_chargevoltage(bq, val1);
|
||||
bq->cfg.battery_voltage_term = val1;
|
||||
break;
|
||||
case PSY_IIO_SYV_ENABLE_CHAGER_TERM:
|
||||
bq->cfg.enable_term = val1;
|
||||
bq2589x_enable_term(bq, bq->cfg.enable_term);
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported BQ25890 IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
pr_err_ratelimited("Couldn't write IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int bq_iio_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct bq2589x *chip = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *iio_chan = chip->iio_chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(syv690d_iio_psy_channels); i++, iio_chan++)
|
||||
if (iio_chan->channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info syv690d_iio_info = {
|
||||
.read_raw = bq_iio_read_raw,
|
||||
.write_raw = bq_iio_write_raw,
|
||||
.of_xlate = bq_iio_of_xlate,
|
||||
};
|
||||
|
||||
int bq_init_iio_psy(struct bq2589x *chip)
|
||||
{
|
||||
struct iio_dev *indio_dev = chip->indio_dev;
|
||||
struct iio_chan_spec *chan;
|
||||
int num_iio_channels = ARRAY_SIZE(syv690d_iio_psy_channels);
|
||||
int rc, i;
|
||||
|
||||
chip->iio_chan = devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->iio_chan), GFP_KERNEL);
|
||||
if (!chip->iio_chan)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->int_iio_chans =
|
||||
devm_kcalloc(chip->dev, num_iio_channels,
|
||||
sizeof(*chip->int_iio_chans), GFP_KERNEL);
|
||||
if (!chip->int_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &syv690d_iio_info;
|
||||
indio_dev->dev.parent = chip->dev;
|
||||
indio_dev->dev.of_node = chip->dev->of_node;
|
||||
indio_dev->name = "syv690d,mainchg";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip->iio_chan;
|
||||
indio_dev->num_channels = num_iio_channels;
|
||||
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
chip->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &chip->iio_chan[i];
|
||||
chip->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = syv690d_iio_psy_channels[i].channel_num;
|
||||
chan->type = syv690d_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
syv690d_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name = syv690d_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate =
|
||||
syv690d_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
|
||||
rc = devm_iio_device_register(chip->dev, indio_dev);
|
||||
if (rc)
|
||||
pr_err("Failed to register QG IIO device, rc=%d\n", rc);
|
||||
|
||||
pr_err("BQ25890H IIO device, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
4
drivers/power/supply/xiaomi/platform/external/pd/Makefile
vendored
Normal file
4
drivers/power/supply/xiaomi/platform/external/pd/Makefile
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_PD_RT17XX) += rt17xx_drv.o
|
||||
rt17xx_drv-objs := rt17xx_iio.o xm_adapter_class.o xm_pd_adapter.o
|
59
drivers/power/supply/xiaomi/platform/external/pd/inc/rt17xx_iio.h
vendored
Normal file
59
drivers/power/supply/xiaomi/platform/external/pd/inc/rt17xx_iio.h
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
#ifndef RT17XX_IIO_H
|
||||
#define RT17XX_IIO_H
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/qti_power_supply.h>
|
||||
#include <dt-bindings/iio/qti_power_supply_iio.h>
|
||||
|
||||
struct rt17xx_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define RT17XX_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define RT17XX_CHAN_ENERGY(_name, _num) \
|
||||
RT17XX_IIO_CHAN(_name, _num, IIO_ENERGY, BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
static const struct rt17xx_iio_channels rt17xx_iio_psy_channels[] = {
|
||||
RT17XX_CHAN_ENERGY("rt_pd_active", PSY_IIO_RT_PD_ACTIVE) RT17XX_CHAN_ENERGY(
|
||||
"rt_pd_current_max",
|
||||
PSY_IIO_RT_PD_CURRENT_MAX) RT17XX_CHAN_ENERGY("rt_pd_voltage_min",
|
||||
PSY_IIO_RT_PD_VOLTAGE_MIN)
|
||||
RT17XX_CHAN_ENERGY("rt_pd_voltage_max", PSY_IIO_RT_PD_VOLTAGE_MAX) RT17XX_CHAN_ENERGY(
|
||||
"rt_pd_in_hard_reset",
|
||||
PSY_IIO_RT_PD_IN_HARD_RESET) RT17XX_CHAN_ENERGY("rt_typec_cc_orientation",
|
||||
PSY_IIO_RT_TYPEC_CC_ORIENTATION)
|
||||
RT17XX_CHAN_ENERGY("rt_typec_mode", PSY_IIO_RT_TYPEC_MODE) RT17XX_CHAN_ENERGY(
|
||||
"rt_pd_usb_suspend_supported",
|
||||
PSY_IIO_RT_PD_USB_SUSPEND_SUPPORTED)
|
||||
RT17XX_CHAN_ENERGY("rt_pd_apdo_volt_max",
|
||||
PSY_IIO_RT_PD_APDO_VOLT_MAX)
|
||||
RT17XX_CHAN_ENERGY(
|
||||
"rt_pd_apdo_curr_max",
|
||||
PSY_IIO_RT_PD_APDO_CURR_MAX)
|
||||
RT17XX_CHAN_ENERGY(
|
||||
"rt_pd_usb_real_type",
|
||||
PSY_IIO_RT_PD_USB_REAL_TYPE)
|
||||
RT17XX_CHAN_ENERGY(
|
||||
"rt_typec_accessory_mode",
|
||||
PSY_IIO_RT_TYPEC_ACCESSORY_MODE)
|
||||
RT17XX_CHAN_ENERGY(
|
||||
"rt_typec_adapter_id",
|
||||
PSY_IIO_RT_TYPEC_ADAPTER_ID)
|
||||
};
|
||||
|
||||
int rt17xx_init_iio_psy(struct xm_pd_adapter_info *info);
|
||||
|
||||
#endif /*RT17XX_IIO_H*/
|
18
drivers/power/supply/xiaomi/platform/external/pd/inc/xm_adapter_class.h
vendored
Normal file
18
drivers/power/supply/xiaomi/platform/external/pd/inc/xm_adapter_class.h
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
#ifndef XM_ADAPTER_CLASS_H
|
||||
#define XM_ADAPTER_CLASS_H
|
||||
|
||||
#define PD_ROLE_SINK_FOR_ADAPTER 0
|
||||
#define PD_ROLE_SOURCE_FOR_ADAPTER 1
|
||||
|
||||
#define to_adapter_device(obj) container_of(obj, struct adapter_device, dev)
|
||||
struct adapter_device *
|
||||
adapter_device_register(const char *name, struct device *parent, void *devdata,
|
||||
const struct adapter_ops *ops,
|
||||
const struct adapter_properties *props);
|
||||
void adapter_device_unregister(struct adapter_device *adapter_dev);
|
||||
struct adapter_device *get_adapter_by_name(const char *name);
|
||||
void adapter_class_exit(void);
|
||||
int adapter_class_init(void);
|
||||
|
||||
#endif /*XM_ADAPTER_CLASS_H*/
|
166
drivers/power/supply/xiaomi/platform/external/pd/inc/xm_pd_adapter.h
vendored
Normal file
166
drivers/power/supply/xiaomi/platform/external/pd/inc/xm_pd_adapter.h
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
#ifndef XM_PD_ADAPTER_H
|
||||
#define XM_PD_ADAPTER_H
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/tcpc/tcpm.h>
|
||||
#include <linux/usb/tcpc/tcpci_config.h>
|
||||
#include <linux/battmngr/battmngr_notifier.h>
|
||||
|
||||
#define ADAPTER_CAP_MAX_NR 10
|
||||
|
||||
extern struct rt1711_chip *g_tcpc_rt1711h;
|
||||
|
||||
struct adapter_power_cap {
|
||||
uint8_t selected_cap_idx;
|
||||
uint8_t nr;
|
||||
uint8_t pdp;
|
||||
uint8_t pwr_limit[ADAPTER_CAP_MAX_NR];
|
||||
int max_mv[ADAPTER_CAP_MAX_NR];
|
||||
int min_mv[ADAPTER_CAP_MAX_NR];
|
||||
int ma[ADAPTER_CAP_MAX_NR];
|
||||
int maxwatt[ADAPTER_CAP_MAX_NR];
|
||||
int minwatt[ADAPTER_CAP_MAX_NR];
|
||||
uint8_t type[ADAPTER_CAP_MAX_NR];
|
||||
int info[ADAPTER_CAP_MAX_NR];
|
||||
};
|
||||
|
||||
struct adapter_properties {
|
||||
const char *alias_name;
|
||||
};
|
||||
|
||||
enum uvdm_state {
|
||||
USBPD_UVDM_DISCONNECT,
|
||||
USBPD_UVDM_CHARGER_VERSION,
|
||||
USBPD_UVDM_CHARGER_VOLTAGE,
|
||||
USBPD_UVDM_CHARGER_TEMP,
|
||||
USBPD_UVDM_SESSION_SEED,
|
||||
USBPD_UVDM_AUTHENTICATION,
|
||||
USBPD_UVDM_VERIFIED,
|
||||
USBPD_UVDM_REMOVE_COMPENSATION,
|
||||
USBPD_UVDM_REVERSE_AUTHEN,
|
||||
USBPD_UVDM_CONNECT,
|
||||
USBPD_UVDM_NAN_ACK,
|
||||
};
|
||||
|
||||
enum adapter_cap_type {
|
||||
XM_PD,
|
||||
XM_PD_APDO,
|
||||
XM_PD_APDO_REGAIN,
|
||||
XM_CAP_TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
#define USB_PD_MI_SVID 0x2717
|
||||
#define USBPD_UVDM_SS_LEN 4
|
||||
#define USBPD_UVDM_VERIFIED_LEN 1
|
||||
#define USBPD_VDM_REQUEST 0x1
|
||||
|
||||
#define VDM_HDR(svid, cmd0, cmd1) \
|
||||
(((svid) << 16) | (0 << 15) | ((cmd0) << 8) | (cmd1))
|
||||
#define UVDM_HDR_CMD(hdr) ((hdr) & 0xFF)
|
||||
|
||||
struct usbpd_vdm_data {
|
||||
int ta_version;
|
||||
int ta_temp;
|
||||
int ta_voltage;
|
||||
bool reauth;
|
||||
unsigned long s_secert[USBPD_UVDM_SS_LEN];
|
||||
unsigned long digest[USBPD_UVDM_SS_LEN];
|
||||
};
|
||||
|
||||
struct adapter_device {
|
||||
struct adapter_properties props;
|
||||
const struct adapter_ops *ops;
|
||||
struct mutex ops_lock;
|
||||
struct device dev;
|
||||
struct srcu_notifier_head evt_nh;
|
||||
void *driver_data;
|
||||
uint32_t adapter_svid;
|
||||
uint32_t adapter_id;
|
||||
uint32_t adapter_fw_ver;
|
||||
uint32_t adapter_hw_ver;
|
||||
struct usbpd_vdm_data vdm_data;
|
||||
int uvdm_state;
|
||||
bool verify_process;
|
||||
bool verifed;
|
||||
uint8_t role;
|
||||
uint8_t current_state;
|
||||
uint32_t received_pdos[7];
|
||||
};
|
||||
|
||||
struct adapter_ops {
|
||||
int (*suspend)(struct adapter_device *dev, pm_message_t state);
|
||||
int (*resume)(struct adapter_device *dev);
|
||||
int (*get_cap)(struct adapter_device *dev, enum adapter_cap_type type,
|
||||
struct adapter_power_cap *cap);
|
||||
int (*get_svid)(struct adapter_device *dev);
|
||||
int (*request_vdm_cmd)(struct adapter_device *dev, enum uvdm_state cmd,
|
||||
unsigned char *data, unsigned int data_len);
|
||||
int (*get_power_role)(struct adapter_device *dev);
|
||||
int (*get_current_state)(struct adapter_device *dev);
|
||||
int (*get_pdos)(struct adapter_device *dev);
|
||||
int (*set_pd_verify_process)(struct adapter_device *dev,
|
||||
int verify_in_process);
|
||||
};
|
||||
|
||||
struct xm_pd_adapter_info {
|
||||
struct device *dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct iio_channel *int_iio_chans;
|
||||
|
||||
struct tcpc_device *tcpc;
|
||||
struct notifier_block pd_nb;
|
||||
struct adapter_device *adapter_dev;
|
||||
struct task_struct *adapter_task;
|
||||
const char *adapter_dev_name;
|
||||
bool enable_kpoc_shdn;
|
||||
struct tcpm_svid_list *adapter_svid_list;
|
||||
struct power_supply *usb_psy;
|
||||
struct power_supply *batt_psy;
|
||||
|
||||
int pd_active;
|
||||
int pd_cur_max;
|
||||
int pd_vol_min;
|
||||
int pd_vol_max;
|
||||
int pd_in_hard_reset;
|
||||
int typec_cc_orientation;
|
||||
int typec_mode;
|
||||
int pd_usb_suspend_supported;
|
||||
int pd_apdo_volt_max;
|
||||
int pd_apdo_curr_max;
|
||||
int pd_usb_real_type;
|
||||
int typec_accessory_mode;
|
||||
};
|
||||
|
||||
static inline void *
|
||||
adapter_dev_get_drvdata(const struct adapter_device *adapter_dev)
|
||||
{
|
||||
return adapter_dev->driver_data;
|
||||
}
|
||||
|
||||
static inline void adapter_dev_set_drvdata(struct adapter_device *adapter_dev,
|
||||
void *data)
|
||||
{
|
||||
adapter_dev->driver_data = data;
|
||||
}
|
||||
|
||||
int adapter_check_usb_psy(struct xm_pd_adapter_info *info);
|
||||
int adapter_check_battery_psy(struct xm_pd_adapter_info *info);
|
||||
|
||||
#endif /*XM_PD_ADAPTER_H*/
|
216
drivers/power/supply/xiaomi/platform/external/pd/rt17xx_iio.c
vendored
Normal file
216
drivers/power/supply/xiaomi/platform/external/pd/rt17xx_iio.c
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
|
||||
#include "inc/xm_pd_adapter.h"
|
||||
#include "inc/xm_adapter_class.h"
|
||||
#include "inc/rt17xx_iio.h"
|
||||
|
||||
static int rt17xx_iio_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct xm_pd_adapter_info *info = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_RT_PD_ACTIVE:
|
||||
if (info->pd_active == val1)
|
||||
break;
|
||||
info->pd_active = val1;
|
||||
if (adapter_check_usb_psy(info) &&
|
||||
adapter_check_battery_psy(info)) {
|
||||
g_battmngr_noti->pd_msg.msg_type =
|
||||
BATTMNGR_MSG_PD_ACTIVE;
|
||||
g_battmngr_noti->pd_msg.pd_active = info->pd_active;
|
||||
battmngr_notifier_call_chain(BATTMNGR_EVENT_PD,
|
||||
g_battmngr_noti);
|
||||
pr_err("%s pd_active: %d\n", __func__, info->pd_active);
|
||||
}
|
||||
break;
|
||||
case PSY_IIO_RT_PD_CURRENT_MAX:
|
||||
info->pd_cur_max = val1;
|
||||
g_battmngr_noti->pd_msg.pd_curr_max = info->pd_cur_max;
|
||||
pr_err("%s pd_curr_max: %d\n", __func__, info->pd_cur_max);
|
||||
break;
|
||||
case PSY_IIO_RT_PD_VOLTAGE_MIN:
|
||||
info->pd_vol_min = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_VOLTAGE_MAX:
|
||||
info->pd_vol_max = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_IN_HARD_RESET:
|
||||
info->pd_in_hard_reset = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_CC_ORIENTATION:
|
||||
info->typec_cc_orientation = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_MODE:
|
||||
info->typec_mode = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_USB_SUSPEND_SUPPORTED:
|
||||
info->pd_usb_suspend_supported = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_APDO_VOLT_MAX:
|
||||
info->pd_apdo_volt_max = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_APDO_CURR_MAX:
|
||||
info->pd_apdo_curr_max = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_USB_REAL_TYPE:
|
||||
info->pd_usb_real_type = val1;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_ACCESSORY_MODE:
|
||||
if (info->typec_accessory_mode == val1)
|
||||
break;
|
||||
info->typec_accessory_mode = val1;
|
||||
pr_err("%s typec_accessory_mode: %d\n", __func__,
|
||||
info->typec_accessory_mode);
|
||||
g_battmngr_noti->pd_msg.msg_type = BATTMNGR_MSG_PD_AUDIO;
|
||||
g_battmngr_noti->pd_msg.accessory_mode =
|
||||
info->typec_accessory_mode;
|
||||
battmngr_notifier_call_chain(BATTMNGR_EVENT_PD,
|
||||
g_battmngr_noti);
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported rt17xx IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
pr_err_ratelimited("Couldn't write IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rt17xx_iio_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct xm_pd_adapter_info *info = iio_priv(indio_dev);
|
||||
int rc = 0;
|
||||
|
||||
*val1 = 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case PSY_IIO_RT_PD_ACTIVE:
|
||||
*val1 = info->pd_active;
|
||||
;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_CURRENT_MAX:
|
||||
*val1 = info->pd_cur_max;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_VOLTAGE_MIN:
|
||||
*val1 = info->pd_vol_min;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_VOLTAGE_MAX:
|
||||
*val1 = info->pd_vol_max;
|
||||
;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_IN_HARD_RESET:
|
||||
*val1 = info->pd_in_hard_reset;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_CC_ORIENTATION:
|
||||
*val1 = info->typec_cc_orientation;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_MODE:
|
||||
*val1 = info->typec_mode;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_USB_SUSPEND_SUPPORTED:
|
||||
*val1 = info->pd_usb_suspend_supported;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_APDO_VOLT_MAX:
|
||||
*val1 = info->pd_apdo_volt_max;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_APDO_CURR_MAX:
|
||||
*val1 = info->pd_apdo_curr_max;
|
||||
break;
|
||||
case PSY_IIO_RT_PD_USB_REAL_TYPE:
|
||||
*val1 = info->pd_usb_real_type;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_ACCESSORY_MODE:
|
||||
*val1 = info->typec_accessory_mode;
|
||||
break;
|
||||
case PSY_IIO_RT_TYPEC_ADAPTER_ID:
|
||||
*val1 = info->adapter_dev->adapter_id;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unsupported rt17xx IIO chan %d\n", chan->channel);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
pr_err_ratelimited("Couldn't read IIO channel %d, rc = %d\n",
|
||||
chan->channel, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int rt17xx_iio_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct xm_pd_adapter_info *chip = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *iio_chan = chip->iio_chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rt17xx_iio_psy_channels); i++, iio_chan++)
|
||||
if (iio_chan->channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info rt17xx_iio_info = {
|
||||
.read_raw = rt17xx_iio_read_raw,
|
||||
.write_raw = rt17xx_iio_write_raw,
|
||||
.of_xlate = rt17xx_iio_of_xlate,
|
||||
};
|
||||
|
||||
int rt17xx_init_iio_psy(struct xm_pd_adapter_info *info)
|
||||
{
|
||||
struct iio_dev *indio_dev = info->indio_dev;
|
||||
struct iio_chan_spec *chan;
|
||||
int num_iio_channels = ARRAY_SIZE(rt17xx_iio_psy_channels);
|
||||
int rc, i;
|
||||
|
||||
pr_err("rt17xx_init_iio_psy start\n");
|
||||
info->iio_chan = devm_kcalloc(info->dev, num_iio_channels,
|
||||
sizeof(*info->iio_chan), GFP_KERNEL);
|
||||
if (!info->iio_chan)
|
||||
return -ENOMEM;
|
||||
|
||||
info->int_iio_chans =
|
||||
devm_kcalloc(info->dev, num_iio_channels,
|
||||
sizeof(*info->int_iio_chans), GFP_KERNEL);
|
||||
if (!info->int_iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &rt17xx_iio_info;
|
||||
indio_dev->dev.parent = info->dev;
|
||||
indio_dev->dev.of_node = info->dev->of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->iio_chan;
|
||||
indio_dev->num_channels = num_iio_channels;
|
||||
indio_dev->name = "rt17xx,pd";
|
||||
for (i = 0; i < num_iio_channels; i++) {
|
||||
info->int_iio_chans[i].indio_dev = indio_dev;
|
||||
chan = &info->iio_chan[i];
|
||||
info->int_iio_chans[i].channel = chan;
|
||||
chan->address = i;
|
||||
chan->channel = rt17xx_iio_psy_channels[i].channel_num;
|
||||
chan->type = rt17xx_iio_psy_channels[i].type;
|
||||
chan->datasheet_name =
|
||||
rt17xx_iio_psy_channels[i].datasheet_name;
|
||||
chan->extend_name = rt17xx_iio_psy_channels[i].datasheet_name;
|
||||
chan->info_mask_separate = rt17xx_iio_psy_channels[i].info_mask;
|
||||
}
|
||||
|
||||
rc = devm_iio_device_register(info->dev, indio_dev);
|
||||
if (rc)
|
||||
pr_err("Failed to register rt17xx IIO device, rc=%d\n", rc);
|
||||
|
||||
pr_err("rt17xx IIO device, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
727
drivers/power/supply/xiaomi/platform/external/pd/xm_adapter_class.c
vendored
Normal file
727
drivers/power/supply/xiaomi/platform/external/pd/xm_adapter_class.c
vendored
Normal file
@ -0,0 +1,727 @@
|
||||
|
||||
#include "inc/xm_pd_adapter.h"
|
||||
#include "inc/xm_adapter_class.h"
|
||||
#include "inc/rt17xx_iio.h"
|
||||
|
||||
static struct class *adapter_class;
|
||||
|
||||
static int log_level = 2;
|
||||
#define class_err(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 0) \
|
||||
printk(KERN_ERR "[xm_adapter_class] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define class_info(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 1) \
|
||||
printk(KERN_ERR "[xm_adapter_class] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define class_dbg(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 2) \
|
||||
printk(KERN_ERR "[xm_adapter_class] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static const char *const usbpd_state_strings[] = {
|
||||
"UNKNOWN",
|
||||
/******************* Source *******************/
|
||||
#ifdef CONFIG_USB_PD_PE_SOURCE
|
||||
"SRC_STARTUP",
|
||||
"SRC_DISCOVERY",
|
||||
"SRC_SEND_CAPABILITIES",
|
||||
"SRC_NEGOTIATE_CAPABILITIES",
|
||||
"SRC_TRANSITION_SUPPLY",
|
||||
"SRC_TRANSITION_SUPPLY2",
|
||||
"SRC_Ready",
|
||||
"SRC_DISABLED",
|
||||
"SRC_CAPABILITY_RESPONSE",
|
||||
"SRC_HARD_RESET",
|
||||
"SRC_HARD_RESET_RECEIVED",
|
||||
"SRC_TRANSITION_TO_DEFAULT",
|
||||
"SRC_GET_SINK_CAP",
|
||||
"SRC_WAIT_NEW_CAPABILITIES",
|
||||
"SRC_SEND_SOFT_RESET",
|
||||
"SRC_SOFT_RESET",
|
||||
/* Source Startup Discover Cable */
|
||||
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
"SRC_CBL_SEND_SOFT_RESET",
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
"SRC_VDM_IDENTITY_REQUEST",
|
||||
"SRC_VDM_IDENTITY_ACKED",
|
||||
"SRC_VDM_IDENTITY_NAKED",
|
||||
#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */
|
||||
/* Source for PD30 */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
"SRC_SEND_NOT_SUPPORTED",
|
||||
"SRC_NOT_SUPPORTED_RECEIVED",
|
||||
"SRC_CHUNK_RECEIVED",
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
"SRC_SEND_SOURCE_ALERT",
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
"SRC_SINK_ALERT_RECEIVED",
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
"SRC_GIVE_SOURCE_CAP_EXT",
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
"SRC_GIVE_SOURCE_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
||||
"SRC_GET_SINK_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
|
||||
"SRC_GIVE_PPS_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
#endif /* CONFIG_USB_PD_PE_SOURCE */
|
||||
/******************* Sink *******************/
|
||||
#ifdef CONFIG_USB_PD_PE_SINK
|
||||
/* Sink Init */
|
||||
"SNK_STARTUP",
|
||||
"SNK_DISCOVERY",
|
||||
"SNK_WAIT_FOR_CAPABILITIES",
|
||||
"SNK_EVALUATE_CAPABILITY",
|
||||
"SNK_SELECT_CAPABILITY",
|
||||
"SNK_TRANSITION_SINK",
|
||||
"SNK_Ready",
|
||||
"SNK_HARD_RESET",
|
||||
"SNK_TRANSITION_TO_DEFAULT",
|
||||
"SNK_GIVE_SINK_CAP",
|
||||
"SNK_GET_SOURCE_CAP",
|
||||
|
||||
"SNK_SEND_SOFT_RESET",
|
||||
"SNK_SOFT_RESET",
|
||||
/* Sink for PD30 */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
"SNK_SEND_NOT_SUPPORTED",
|
||||
"SNK_NOT_SUPPORTED_RECEIVED",
|
||||
"SNK_CHUNK_RECEIVED",
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
"SNK_SOURCE_ALERT_RECEIVED",
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
"SNK_SEND_SINK_ALERT",
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
"SNK_GET_SOURCE_CAP_EXT",
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
||||
"SNK_GET_SOURCE_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
"SNK_GIVE_SINK_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
"SNK_GET_PPS_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
#endif /* CONFIG_USB_PD_PE_SINK */
|
||||
/******************* DR_SWAP *******************/
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
/* DR_SWAP_DFP */
|
||||
"DRS_DFP_UFP_EVALUATE_DR_SWAP",
|
||||
"DRS_DFP_UFP_ACCEPT_DR_SWAP",
|
||||
"DRS_DFP_UFP_CHANGE_TO_UFP",
|
||||
"DRS_DFP_UFP_SEND_DR_SWAP",
|
||||
"DRS_DFP_UFP_REJECT_DR_SWAP",
|
||||
/* DR_SWAP_UFP */
|
||||
"DRS_UFP_DFP_EVALUATE_DR_SWAP",
|
||||
"DRS_UFP_DFP_ACCEPT_DR_SWAP",
|
||||
"DRS_UFP_DFP_CHANGE_TO_DFP",
|
||||
"DRS_UFP_DFP_SEND_DR_SWAP",
|
||||
"DRS_UFP_DFP_REJECT_DR_SWAP",
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
/******************* PR_SWAP *******************/
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
/* PR_SWAP_SRC */
|
||||
"PRS_SRC_SNK_EVALUATE_PR_SWAP",
|
||||
"PRS_SRC_SNK_ACCEPT_PR_SWAP",
|
||||
"PRS_SRC_SNK_TRANSITION_TO_OFF",
|
||||
"PRS_SRC_SNK_ASSERT_RD",
|
||||
"PRS_SRC_SNK_WAIT_SOURCE_ON",
|
||||
"PRS_SRC_SNK_SEND_SWAP",
|
||||
"PRS_SRC_SNK_REJECT_PR_SWAP",
|
||||
/* PR_SWAP_SNK */
|
||||
"PRS_SNK_SRC_EVALUATE_PR_SWAP",
|
||||
"PRS_SNK_SRC_ACCEPT_PR_SWAP",
|
||||
"PRS_SNK_SRC_TRANSITION_TO_OFF",
|
||||
"PRS_SNK_SRC_ASSERT_RP",
|
||||
"PRS_SNK_SRC_SOURCE_ON",
|
||||
"PRS_SNK_SRC_SEND_SWAP",
|
||||
"PRS_SNK_SRC_REJECT_SWAP",
|
||||
/* get same role cap */
|
||||
"DR_SRC_GET_SOURCE_CAP",
|
||||
"DR_SRC_GIVE_SINK_CAP",
|
||||
"DR_SNK_GET_SINK_CAP",
|
||||
"DR_SNK_GIVE_SOURCE_CAP",
|
||||
/* get same role cap for PD30 */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
"DR_SNK_GIVE_SOURCE_CAP_EXT",
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
"DR_SRC_GET_SOURCE_CAP_EXT",
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
/******************* VCONN_SWAP *******************/
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
"VCS_SEND_SWAP",
|
||||
"VCS_EVALUATE_SWAP",
|
||||
"VCS_ACCEPT_SWAP",
|
||||
"VCS_REJECT_VCONN_SWAP",
|
||||
"VCS_WAIT_FOR_VCONN",
|
||||
"VCS_TURN_OFF_VCONN",
|
||||
"VCS_TURN_ON_VCONN",
|
||||
"VCS_SEND_PS_RDY",
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
/******************* UFP_VDM *******************/
|
||||
"UFP_VDM_GET_IDENTITY",
|
||||
"UFP_VDM_GET_SVIDS",
|
||||
"UFP_VDM_GET_MODES",
|
||||
"UFP_VDM_EVALUATE_MODE_ENTRY",
|
||||
"UFP_VDM_MODE_EXIT",
|
||||
"UFP_VDM_ATTENTION_REQUEST",
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE
|
||||
"UFP_VDM_DP_STATUS_UPDATE",
|
||||
"UFP_VDM_DP_CONFIGURE",
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE */
|
||||
/******************* DFP_VDM *******************/
|
||||
"DFP_UFP_VDM_IDENTITY_REQUEST",
|
||||
"DFP_UFP_VDM_IDENTITY_ACKED",
|
||||
"DFP_UFP_VDM_IDENTITY_NAKED",
|
||||
"DFP_CBL_VDM_IDENTITY_REQUEST",
|
||||
"DFP_CBL_VDM_IDENTITY_ACKED",
|
||||
"DFP_CBL_VDM_IDENTITY_NAKED",
|
||||
"DFP_VDM_SVIDS_REQUEST",
|
||||
"DFP_VDM_SVIDS_ACKED",
|
||||
"DFP_VDM_SVIDS_NAKED",
|
||||
"DFP_VDM_MODES_REQUEST",
|
||||
"DFP_VDM_MODES_ACKED",
|
||||
"DFP_VDM_MODES_NAKED",
|
||||
"DFP_VDM_MODE_ENTRY_REQUEST",
|
||||
"DFP_VDM_MODE_ENTRY_ACKED",
|
||||
"DFP_VDM_MODE_ENTRY_NAKED",
|
||||
"DFP_VDM_MODE_EXIT_REQUEST",
|
||||
"DFP_VDM_MODE_EXIT_ACKED",
|
||||
"DFP_VDM_ATTENTION_REQUEST",
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
"DFP_CBL_SEND_SOFT_RESET",
|
||||
"DFP_CBL_SEND_CABLE_RESET",
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
||||
"DFP_VDM_DP_STATUS_UPDATE_REQUEST",
|
||||
"DFP_VDM_DP_STATUS_UPDATE_ACKED",
|
||||
"DFP_VDM_DP_STATUS_UPDATE_NAKED",
|
||||
"DFP_VDM_DP_CONFIGURATION_REQUEST",
|
||||
"DFP_VDM_DP_CONFIGURATION_ACKED",
|
||||
"DFP_VDM_DP_CONFIGURATION_NAKED",
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
|
||||
/******************* UVDM & SVDM *******************/
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_VDM
|
||||
"UFP_UVDM_RECV",
|
||||
"DFP_UVDM_SEND",
|
||||
"DFP_UVDM_ACKED",
|
||||
"DFP_UVDM_NAKED",
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
|
||||
/******************* PD30 Common *******************/
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
|
||||
"GET_BATTERY_CAP",
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
|
||||
"GIVE_BATTERY_CAP",
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
|
||||
"GET_BATTERY_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
|
||||
"GIVE_BATTERY_STATUS",
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
|
||||
"GET_MANUFACTURER_INFO",
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
|
||||
"GIVE_MANUFACTURER_INFO",
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
|
||||
"GET_COUNTRY_CODES",
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
|
||||
"GIVE_COUNTRY_CODES",
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
|
||||
"GET_COUNTRY_INFO",
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
|
||||
"GIVE_COUNTRY_INFO",
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
|
||||
"VDM_NOT_SUPPORTED",
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
/******************* Others *******************/
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
"DBG_READY",
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
||||
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
|
||||
"OVER_RECV_HRESET_LIMIT",
|
||||
#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */
|
||||
"REJECT",
|
||||
"ERROR_RECOVERY",
|
||||
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
||||
"ERROR_RECOVERY_ONCE",
|
||||
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
||||
"BIST_TEST_DATA",
|
||||
"BIST_CARRIER_MODE_2",
|
||||
/* Wait tx finished */
|
||||
"IDLE1",
|
||||
"IDLE2",
|
||||
"PD_NR_PE_STATES",
|
||||
};
|
||||
|
||||
static ssize_t adapter_show_name(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
return snprintf(buf, 20, "%s\n",
|
||||
adapter_dev->props.alias_name ?
|
||||
adapter_dev->props.alias_name :
|
||||
"anonymous");
|
||||
}
|
||||
|
||||
static void adapter_device_release(struct device *dev)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
kfree(adapter_dev);
|
||||
}
|
||||
|
||||
static ssize_t adapter_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_svid) {
|
||||
adapter_dev->ops->get_svid(adapter_dev);
|
||||
}
|
||||
class_info("%s: adapter_id is %08x\n", __func__,
|
||||
adapter_dev->adapter_id);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%08x\n", adapter_dev->adapter_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(adapter_id);
|
||||
|
||||
static ssize_t adapter_svid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_svid) {
|
||||
adapter_dev->ops->get_svid(adapter_dev);
|
||||
}
|
||||
class_info("%s: adapter_svid is %04x\n", __func__,
|
||||
adapter_dev->adapter_svid);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%04x\n", adapter_dev->adapter_svid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(adapter_svid);
|
||||
|
||||
static int StringToHex(char *str, unsigned char *out, unsigned int *outlen)
|
||||
{
|
||||
char *p = str;
|
||||
char high = 0, low = 0;
|
||||
int tmplen = strlen(p), cnt = 0;
|
||||
tmplen = strlen(p);
|
||||
while (cnt < (tmplen / 2)) {
|
||||
high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*p - 48 - 7 :
|
||||
*p - 48;
|
||||
low = (*(++p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*(p)-48 - 7 :
|
||||
*(p)-48;
|
||||
out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));
|
||||
p++;
|
||||
cnt++;
|
||||
}
|
||||
if (tmplen % 2 != 0)
|
||||
out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ?
|
||||
*p - 48 - 7 :
|
||||
*p - 48;
|
||||
|
||||
if (outlen != NULL)
|
||||
*outlen = tmplen / 2 + tmplen % 2;
|
||||
|
||||
return tmplen / 2 + tmplen % 2;
|
||||
}
|
||||
|
||||
static ssize_t request_vdm_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
int cmd, ret;
|
||||
unsigned char buffer[64];
|
||||
unsigned char *data;
|
||||
unsigned int count;
|
||||
int i;
|
||||
|
||||
if (in_interrupt()) {
|
||||
data = kmalloc(40, GFP_ATOMIC);
|
||||
class_info("%s: kmalloc atomic ok.\n", __func__);
|
||||
} else {
|
||||
data = kmalloc(40, GFP_KERNEL);
|
||||
class_info("%s: kmalloc kernel ok.\n", __func__);
|
||||
}
|
||||
memset(data, 0, 40);
|
||||
|
||||
ret = sscanf(buf, "%d,%s\n", &cmd, buffer);
|
||||
class_info("%s:cmd:%d, buffer:%s\n", __func__, cmd, buffer);
|
||||
|
||||
StringToHex(buffer, data, &count);
|
||||
class_info("%s:count = %d\n", __func__, count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
class_info("%02x", data[i]);
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->request_vdm_cmd) {
|
||||
adapter_dev->ops->request_vdm_cmd(adapter_dev, cmd, data,
|
||||
count);
|
||||
}
|
||||
kfree(data);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t request_vdm_cmd_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
int i;
|
||||
char data[16], str_buf[128] = { 0 };
|
||||
int cmd = adapter_dev->uvdm_state;
|
||||
|
||||
switch (cmd) {
|
||||
case USBPD_UVDM_CHARGER_VERSION:
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%x\n", cmd,
|
||||
adapter_dev->vdm_data.ta_version);
|
||||
case USBPD_UVDM_CHARGER_TEMP:
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d\n", cmd,
|
||||
adapter_dev->vdm_data.ta_temp);
|
||||
case USBPD_UVDM_CHARGER_VOLTAGE:
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d\n", cmd,
|
||||
adapter_dev->vdm_data.ta_voltage);
|
||||
case USBPD_UVDM_SESSION_SEED:
|
||||
case USBPD_UVDM_CONNECT:
|
||||
case USBPD_UVDM_DISCONNECT:
|
||||
case USBPD_UVDM_VERIFIED:
|
||||
case USBPD_UVDM_REMOVE_COMPENSATION:
|
||||
case USBPD_UVDM_NAN_ACK:
|
||||
return snprintf(buf, PAGE_SIZE, "%d,Null\n", cmd);
|
||||
case USBPD_UVDM_REVERSE_AUTHEN:
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%d", cmd,
|
||||
adapter_dev->vdm_data.reauth);
|
||||
case USBPD_UVDM_AUTHENTICATION:
|
||||
for (i = 0; i < USBPD_UVDM_SS_LEN; i++) {
|
||||
memset(data, 0, sizeof(data));
|
||||
snprintf(data, sizeof(data), "%08lx",
|
||||
adapter_dev->vdm_data.digest[i]);
|
||||
strlcat(str_buf, data, sizeof(str_buf));
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%s\n", cmd, str_buf);
|
||||
default:
|
||||
class_err("feedbak cmd:%d is not support\n", cmd);
|
||||
break;
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%d,%s\n", cmd, str_buf);
|
||||
}
|
||||
static DEVICE_ATTR_RW(request_vdm_cmd);
|
||||
|
||||
static ssize_t verify_process_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
int val;
|
||||
|
||||
if (sscanf(buf, "%d\n", &val) != 1) {
|
||||
adapter_dev->verify_process = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adapter_dev->verify_process = !!val;
|
||||
|
||||
class_info("%s: batterysecret verify process :%d\n", __func__,
|
||||
adapter_dev->verify_process);
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->set_pd_verify_process) {
|
||||
adapter_dev->ops->set_pd_verify_process(
|
||||
adapter_dev, adapter_dev->verify_process);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t verify_process_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", adapter_dev->verify_process);
|
||||
}
|
||||
static DEVICE_ATTR_RW(verify_process);
|
||||
|
||||
static ssize_t usbpd_verifed_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
int val = 0;
|
||||
struct adapter_power_cap cap = { 0 };
|
||||
|
||||
if (sscanf(buf, "%d\n", &val) != 1) {
|
||||
adapter_dev->verifed = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
class_info("%s: batteryd set usbpd verifyed :%d\n", __func__, val);
|
||||
adapter_dev->verifed = !!val;
|
||||
|
||||
if (adapter_dev->verifed) {
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_cap) {
|
||||
adapter_dev->ops->get_cap(adapter_dev,
|
||||
XM_PD_APDO_REGAIN, &cap);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t usbpd_verifed_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", adapter_dev->verifed);
|
||||
}
|
||||
static DEVICE_ATTR_RW(usbpd_verifed);
|
||||
|
||||
static ssize_t current_pr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
const char *pr = "none";
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_power_role) {
|
||||
adapter_dev->ops->get_power_role(adapter_dev);
|
||||
}
|
||||
class_info("%s: current_pr is %d\n", __func__, adapter_dev->role);
|
||||
if (adapter_dev->role == PD_ROLE_SINK_FOR_ADAPTER)
|
||||
pr = "sink";
|
||||
else if (adapter_dev->role == PD_ROLE_SOURCE_FOR_ADAPTER)
|
||||
pr = "source";
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", pr);
|
||||
}
|
||||
static DEVICE_ATTR_RO(current_pr);
|
||||
|
||||
static ssize_t current_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_current_state) {
|
||||
adapter_dev->ops->get_current_state(adapter_dev);
|
||||
}
|
||||
class_err("%s: current_state is %d\n", __func__,
|
||||
adapter_dev->current_state);
|
||||
|
||||
if (adapter_dev->current_state >=
|
||||
(sizeof(usbpd_state_strings) / sizeof(usbpd_state_strings[0])))
|
||||
adapter_dev->current_state = 0;
|
||||
|
||||
class_err("%s: %s\n", __func__,
|
||||
usbpd_state_strings[adapter_dev->current_state]);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
usbpd_state_strings[adapter_dev->current_state]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(current_state);
|
||||
|
||||
static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
#define PDO_ATTR(n) \
|
||||
{ \
|
||||
.attr = { .name = __stringify(pdo##n), .mode = 0444 }, \
|
||||
.show = pdo_n_show, \
|
||||
}
|
||||
|
||||
static struct device_attribute dev_attr_pdos[] = {
|
||||
PDO_ATTR(1), PDO_ATTR(2), PDO_ATTR(3), PDO_ATTR(4),
|
||||
PDO_ATTR(5), PDO_ATTR(6), PDO_ATTR(7),
|
||||
};
|
||||
static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
||||
int i;
|
||||
|
||||
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
||||
adapter_dev->ops->get_pdos) {
|
||||
adapter_dev->ops->get_pdos(adapter_dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev_attr_pdos); i++) {
|
||||
if (attr == &dev_attr_pdos[i])
|
||||
/* dump the PDO as a hex string */
|
||||
return snprintf(buf, PAGE_SIZE, "%08x\n",
|
||||
adapter_dev->received_pdos[i]);
|
||||
}
|
||||
|
||||
class_err("%s: Invalid PDO index\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, 0444, adapter_show_name, NULL);
|
||||
|
||||
static struct attribute *adapter_class_attrs[] = {
|
||||
&dev_attr_name.attr, &dev_attr_request_vdm_cmd.attr,
|
||||
&dev_attr_current_state.attr, &dev_attr_adapter_id.attr,
|
||||
&dev_attr_adapter_svid.attr, &dev_attr_verify_process.attr,
|
||||
&dev_attr_usbpd_verifed.attr, &dev_attr_current_pr.attr,
|
||||
&dev_attr_pdos[0].attr, &dev_attr_pdos[1].attr,
|
||||
&dev_attr_pdos[2].attr, &dev_attr_pdos[3].attr,
|
||||
&dev_attr_pdos[4].attr, &dev_attr_pdos[5].attr,
|
||||
&dev_attr_pdos[6].attr, NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group adapter_group = {
|
||||
.attrs = adapter_class_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *adapter_groups[] = {
|
||||
&adapter_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* adapter_device_register - create and register a new object of
|
||||
* adapter_device class.
|
||||
* @name: the name of the new object
|
||||
* @parent: a pointer to the parent device
|
||||
* @devdata: an optional pointer to be stored for private driver use.
|
||||
* The methods may retrieve it by using adapter_get_data(adapter_dev).
|
||||
* @ops: the charger operations structure.
|
||||
*
|
||||
* Creates and registers new charger device. Returns either an
|
||||
* ERR_PTR() or a pointer to the newly allocated device.
|
||||
*/
|
||||
struct adapter_device *
|
||||
adapter_device_register(const char *name, struct device *parent, void *devdata,
|
||||
const struct adapter_ops *ops,
|
||||
const struct adapter_properties *props)
|
||||
{
|
||||
struct adapter_device *adapter_dev = NULL;
|
||||
static struct lock_class_key key;
|
||||
struct srcu_notifier_head *head = NULL;
|
||||
int rc;
|
||||
|
||||
class_err("%s: name=%s\n", __func__, name);
|
||||
adapter_dev = kzalloc(sizeof(*adapter_dev), GFP_KERNEL);
|
||||
if (!adapter_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&adapter_dev->ops_lock);
|
||||
adapter_dev->dev.class = adapter_class;
|
||||
adapter_dev->dev.parent = parent;
|
||||
adapter_dev->dev.release = adapter_device_release;
|
||||
dev_set_name(&adapter_dev->dev, "%s", name);
|
||||
dev_set_drvdata(&adapter_dev->dev, devdata);
|
||||
head = &adapter_dev->evt_nh;
|
||||
srcu_init_notifier_head(head);
|
||||
/* Rename srcu's lock to avoid LockProve warning */
|
||||
lockdep_init_map(&(&head->srcu)->dep_map, name, &key, 0);
|
||||
|
||||
/* Copy properties */
|
||||
if (props) {
|
||||
memcpy(&adapter_dev->props, props,
|
||||
sizeof(struct adapter_properties));
|
||||
}
|
||||
rc = device_register(&adapter_dev->dev);
|
||||
if (rc) {
|
||||
kfree(adapter_dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
adapter_dev->ops = ops;
|
||||
return adapter_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* adapter_device_unregister - unregisters a switching charger device
|
||||
* object.
|
||||
* @adapter_dev: the switching charger device object to be unregistered
|
||||
* and freed.
|
||||
*
|
||||
* Unregisters a previously registered via adapter_device_register object.
|
||||
*/
|
||||
void adapter_device_unregister(struct adapter_device *adapter_dev)
|
||||
{
|
||||
if (!adapter_dev)
|
||||
return;
|
||||
|
||||
mutex_lock(&adapter_dev->ops_lock);
|
||||
adapter_dev->ops = NULL;
|
||||
mutex_unlock(&adapter_dev->ops_lock);
|
||||
device_unregister(&adapter_dev->dev);
|
||||
}
|
||||
|
||||
static int adapter_match_device_by_name(struct device *dev, const void *data)
|
||||
{
|
||||
const char *name = data;
|
||||
|
||||
return strcmp(dev_name(dev), name) == 0;
|
||||
}
|
||||
|
||||
struct adapter_device *get_adapter_by_name(const char *name)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
|
||||
if (!name)
|
||||
return (struct adapter_device *)NULL;
|
||||
dev = class_find_device(adapter_class, NULL, name,
|
||||
adapter_match_device_by_name);
|
||||
|
||||
return dev ? to_adapter_device(dev) : NULL;
|
||||
}
|
||||
|
||||
void adapter_class_exit(void)
|
||||
{
|
||||
class_destroy(adapter_class);
|
||||
}
|
||||
|
||||
int adapter_class_init(void)
|
||||
{
|
||||
adapter_class = class_create(THIS_MODULE, "Charging_Adapter");
|
||||
if (IS_ERR(adapter_class)) {
|
||||
class_err(
|
||||
"Unable to create Charging Adapter class; errno = %ld\n",
|
||||
PTR_ERR(adapter_class));
|
||||
return PTR_ERR(adapter_class);
|
||||
}
|
||||
adapter_class->dev_groups = adapter_groups;
|
||||
|
||||
return 0;
|
||||
}
|
710
drivers/power/supply/xiaomi/platform/external/pd/xm_pd_adapter.c
vendored
Normal file
710
drivers/power/supply/xiaomi/platform/external/pd/xm_pd_adapter.c
vendored
Normal file
@ -0,0 +1,710 @@
|
||||
|
||||
#include "inc/xm_pd_adapter.h"
|
||||
#include "inc/xm_adapter_class.h"
|
||||
#include "inc/rt17xx_iio.h"
|
||||
|
||||
#define PROBE_CNT_MAX 50
|
||||
int get_apdo_regain;
|
||||
struct xm_pd_adapter_info *g_xm_pd_adapter;
|
||||
EXPORT_SYMBOL(g_xm_pd_adapter);
|
||||
|
||||
static int log_level = 2;
|
||||
#define adapter_err(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 0) \
|
||||
printk(KERN_ERR "[xm_pd_adapter] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define adapter_info(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 1) \
|
||||
printk(KERN_ERR "[xm_pd_adapter] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define adapter_dbg(fmt, ...) \
|
||||
do { \
|
||||
if (log_level >= 2) \
|
||||
printk(KERN_ERR "[xm_pd_adapter] " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
int adapter_check_usb_psy(struct xm_pd_adapter_info *info)
|
||||
{
|
||||
if (!info->usb_psy) {
|
||||
info->usb_psy = power_supply_get_by_name("usb");
|
||||
if (!info->usb_psy) {
|
||||
pr_err("usb psy not found!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int adapter_check_battery_psy(struct xm_pd_adapter_info *info)
|
||||
{
|
||||
if (!info->batt_psy) {
|
||||
info->batt_psy = power_supply_get_by_name("battery");
|
||||
if (!info->batt_psy) {
|
||||
pr_err("batt psy not found!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usbpd_mi_vdm_received(struct xm_pd_adapter_info *pinfo,
|
||||
struct tcp_ny_uvdm uvdm)
|
||||
{
|
||||
int i, cmd;
|
||||
|
||||
if (uvdm.uvdm_svid != USB_PD_MI_SVID)
|
||||
return;
|
||||
|
||||
cmd = UVDM_HDR_CMD(uvdm.uvdm_data[0]);
|
||||
adapter_info("cmd = %d\n", cmd);
|
||||
|
||||
adapter_info(
|
||||
"uvdm.ack: %d, uvdm.uvdm_cnt: %d, uvdm.uvdm_svid: 0x%04x\n",
|
||||
uvdm.ack, uvdm.uvdm_cnt, uvdm.uvdm_svid);
|
||||
|
||||
switch (cmd) {
|
||||
case USBPD_UVDM_CHARGER_VERSION:
|
||||
pinfo->adapter_dev->vdm_data.ta_version = uvdm.uvdm_data[1];
|
||||
adapter_info("ta_version:%x\n",
|
||||
pinfo->adapter_dev->vdm_data.ta_version);
|
||||
break;
|
||||
case USBPD_UVDM_CHARGER_TEMP:
|
||||
pinfo->adapter_dev->vdm_data.ta_temp =
|
||||
(uvdm.uvdm_data[1] & 0xFFFF) * 10;
|
||||
adapter_info("pinfo->adapter_dev->vdm_data.ta_temp:%d\n",
|
||||
pinfo->adapter_dev->vdm_data.ta_temp);
|
||||
break;
|
||||
case USBPD_UVDM_CHARGER_VOLTAGE:
|
||||
pinfo->adapter_dev->vdm_data.ta_voltage =
|
||||
(uvdm.uvdm_data[1] & 0xFFFF) * 10;
|
||||
pinfo->adapter_dev->vdm_data.ta_voltage *= 1000; /*V->mV*/
|
||||
adapter_info("ta_voltage:%d\n",
|
||||
pinfo->adapter_dev->vdm_data.ta_voltage);
|
||||
break;
|
||||
case USBPD_UVDM_SESSION_SEED:
|
||||
for (i = 0; i < USBPD_UVDM_SS_LEN; i++) {
|
||||
pinfo->adapter_dev->vdm_data.s_secert[i] =
|
||||
uvdm.uvdm_data[i + 1];
|
||||
adapter_info("usbpd s_secert uvdm.uvdm_data[%d]=0x%x",
|
||||
i + 1, uvdm.uvdm_data[i + 1]);
|
||||
}
|
||||
break;
|
||||
case USBPD_UVDM_AUTHENTICATION:
|
||||
for (i = 0; i < USBPD_UVDM_SS_LEN; i++) {
|
||||
pinfo->adapter_dev->vdm_data.digest[i] =
|
||||
uvdm.uvdm_data[i + 1];
|
||||
adapter_info("usbpd digest[%d]=0x%x", i + 1,
|
||||
uvdm.uvdm_data[i + 1]);
|
||||
}
|
||||
break;
|
||||
case USBPD_UVDM_REVERSE_AUTHEN:
|
||||
pinfo->adapter_dev->vdm_data.reauth =
|
||||
(uvdm.uvdm_data[1] & 0xFFFF);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pinfo->adapter_dev->uvdm_state = cmd;
|
||||
}
|
||||
|
||||
static int pd_tcp_notifier_call(struct notifier_block *pnb, unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct tcp_notify *noti = data;
|
||||
struct xm_pd_adapter_info *pinfo;
|
||||
|
||||
pinfo = container_of(pnb, struct xm_pd_adapter_info, pd_nb);
|
||||
|
||||
adapter_err("PD charger event:%d %d\n", (int)event,
|
||||
(int)noti->pd_state.connected);
|
||||
switch (event) {
|
||||
case TCP_NOTIFY_PD_STATE:
|
||||
switch (noti->pd_state.connected) {
|
||||
case PD_CONNECT_NONE:
|
||||
pinfo->adapter_dev->adapter_id = 0;
|
||||
pinfo->adapter_dev->adapter_svid = 0;
|
||||
pinfo->adapter_dev->uvdm_state = USBPD_UVDM_DISCONNECT;
|
||||
pinfo->adapter_dev->verifed = 0;
|
||||
pinfo->adapter_dev->verify_process = 0;
|
||||
break;
|
||||
case PD_CONNECT_PE_READY_SNK_PD30:
|
||||
pinfo->adapter_dev->uvdm_state = USBPD_UVDM_CONNECT;
|
||||
break;
|
||||
case PD_CONNECT_PE_READY_SNK_APDO:
|
||||
get_apdo_regain = 1;
|
||||
pinfo->adapter_dev->uvdm_state = USBPD_UVDM_CONNECT;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case TCP_NOTIFY_UVDM:
|
||||
adapter_info("%s: tcpc received uvdm message.\n", __func__);
|
||||
usbpd_mi_vdm_received(pinfo, noti->uvdm_msg);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int pd_get_svid(struct adapter_device *dev)
|
||||
{
|
||||
struct xm_pd_adapter_info *info;
|
||||
struct pd_source_cap_ext cap_ext;
|
||||
int ret;
|
||||
int i = 0;
|
||||
uint32_t pd_vdos[8];
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
if (info == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
adapter_info("%s: enter\n", __func__);
|
||||
if (info->adapter_dev->adapter_svid != 0)
|
||||
return 0;
|
||||
|
||||
if (info->adapter_svid_list == NULL) {
|
||||
if (in_interrupt()) {
|
||||
info->adapter_svid_list = kmalloc(
|
||||
sizeof(struct tcpm_svid_list), GFP_ATOMIC);
|
||||
} else {
|
||||
info->adapter_svid_list = kmalloc(
|
||||
sizeof(struct tcpm_svid_list), GFP_KERNEL);
|
||||
}
|
||||
if (info->adapter_svid_list == NULL)
|
||||
adapter_err("[%s] adapter_svid_list is still NULL!\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
ret = tcpm_inquire_pd_partner_inform(info->tcpc, pd_vdos);
|
||||
if (ret == TCPM_SUCCESS) {
|
||||
adapter_info("find adapter id success.\n");
|
||||
for (i = 0; i < 8; i++)
|
||||
adapter_info("VDO[%d] : %08x\n", i, pd_vdos[i]);
|
||||
|
||||
info->adapter_dev->adapter_svid = pd_vdos[0] & 0x0000FFFF;
|
||||
info->adapter_dev->adapter_id = pd_vdos[2] & 0x0000FFFF;
|
||||
adapter_info("adapter_svid = %04x\n",
|
||||
info->adapter_dev->adapter_svid);
|
||||
adapter_info("adapter_id = %08x\n",
|
||||
info->adapter_dev->adapter_id);
|
||||
|
||||
ret = tcpm_inquire_pd_partner_svids(info->tcpc,
|
||||
info->adapter_svid_list);
|
||||
adapter_info("[%s] tcpm_inquire_pd_partner_svids, ret=%d!\n",
|
||||
__func__, ret);
|
||||
if (ret == TCPM_SUCCESS) {
|
||||
adapter_info("discover svid number is %d\n",
|
||||
info->adapter_svid_list->cnt);
|
||||
for (i = 0; i < info->adapter_svid_list->cnt; i++) {
|
||||
adapter_info("SVID[%d] : %04x\n", i,
|
||||
info->adapter_svid_list->svids[i]);
|
||||
if (info->adapter_svid_list->svids[i] ==
|
||||
USB_PD_MI_SVID)
|
||||
info->adapter_dev->adapter_svid =
|
||||
USB_PD_MI_SVID;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = tcpm_dpm_pd_get_source_cap_ext(info->tcpc, NULL,
|
||||
&cap_ext);
|
||||
if (ret == TCPM_SUCCESS) {
|
||||
info->adapter_dev->adapter_svid =
|
||||
cap_ext.vid & 0x0000FFFF;
|
||||
info->adapter_dev->adapter_id =
|
||||
cap_ext.pid & 0x0000FFFF;
|
||||
info->adapter_dev->adapter_fw_ver =
|
||||
cap_ext.fw_ver & 0x0000FFFF;
|
||||
info->adapter_dev->adapter_hw_ver =
|
||||
cap_ext.hw_ver & 0x0000FFFF;
|
||||
adapter_info("adapter_svid = %04x\n",
|
||||
info->adapter_dev->adapter_svid);
|
||||
adapter_info("adapter_id = %08x\n",
|
||||
info->adapter_dev->adapter_id);
|
||||
adapter_info("adapter_fw_ver = %08x\n",
|
||||
info->adapter_dev->adapter_fw_ver);
|
||||
adapter_info("adapter_hw_ver = %08x\n",
|
||||
info->adapter_dev->adapter_hw_ver);
|
||||
} else {
|
||||
adapter_err("[%s] get adapter message failed!\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BSWAP_32(x) \
|
||||
(u32)((((u32)(x) & 0xff000000) >> 24) | \
|
||||
(((u32)(x) & 0x00ff0000) >> 8) | \
|
||||
(((u32)(x) & 0x0000ff00) << 8) | \
|
||||
(((u32)(x) & 0x000000ff) << 24))
|
||||
|
||||
static void usbpd_sha256_bitswap32(unsigned int *array, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
array[i] = BSWAP_32(array[i]);
|
||||
}
|
||||
|
||||
void charToint(char *str, int input_len, unsigned int *out,
|
||||
unsigned int *outlen)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (outlen != NULL)
|
||||
*outlen = 0;
|
||||
for (i = 0; i < (input_len / 4 + 1); i++) {
|
||||
out[i] = ((str[i * 4 + 3] * 0x1000000) |
|
||||
(str[i * 4 + 2] * 0x10000) |
|
||||
(str[i * 4 + 1] * 0x100) | str[i * 4]);
|
||||
*outlen = *outlen + 1;
|
||||
}
|
||||
|
||||
adapter_info("%s: outlen = %d\n", __func__, *outlen);
|
||||
for (i = 0; i < *outlen; i++)
|
||||
adapter_info("%s: out[%d] = %08x\n", __func__, i, out[i]);
|
||||
adapter_info("%s: char to int done.\n", __func__);
|
||||
}
|
||||
|
||||
static int tcp_dpm_event_cb_uvdm(struct tcpc_device *tcpc, int ret,
|
||||
struct tcp_dpm_event *event)
|
||||
{
|
||||
int i;
|
||||
struct tcp_dpm_custom_vdm_data vdm_data = event->tcp_dpm_data.vdm_data;
|
||||
|
||||
adapter_info("%s: vdm_data.cnt = %d\n", __func__, vdm_data.cnt);
|
||||
for (i = 0; i < vdm_data.cnt; i++)
|
||||
adapter_info("%s vdm_data.vdos[%d] = 0x%08x", __func__, i,
|
||||
vdm_data.vdos[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct tcp_dpm_event_cb_data cb_data = {
|
||||
.event_cb = tcp_dpm_event_cb_uvdm,
|
||||
};
|
||||
|
||||
static int pd_request_vdm_cmd(struct adapter_device *dev, enum uvdm_state cmd,
|
||||
unsigned char *data, unsigned int data_len)
|
||||
{
|
||||
u32 vdm_hdr = 0;
|
||||
int rc = 0;
|
||||
struct tcp_dpm_custom_vdm_data *vdm_data;
|
||||
struct xm_pd_adapter_info *info;
|
||||
unsigned int *int_data;
|
||||
unsigned int outlen;
|
||||
int i;
|
||||
|
||||
if (in_interrupt()) {
|
||||
int_data = kmalloc(40, GFP_ATOMIC);
|
||||
vdm_data = kmalloc(sizeof(*vdm_data), GFP_ATOMIC);
|
||||
adapter_info("%s: kmalloc atomic ok.\n", __func__);
|
||||
} else {
|
||||
int_data = kmalloc(40, GFP_KERNEL);
|
||||
vdm_data = kmalloc(sizeof(*vdm_data), GFP_KERNEL);
|
||||
adapter_info("%s: kmalloc kernel ok.\n", __func__);
|
||||
}
|
||||
memset(int_data, 0, 40);
|
||||
|
||||
charToint(data, data_len, int_data, &outlen);
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
if (info == NULL || info->tcpc == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto release_req;
|
||||
}
|
||||
|
||||
vdm_hdr = VDM_HDR(info->adapter_dev->adapter_svid, USBPD_VDM_REQUEST,
|
||||
cmd);
|
||||
vdm_data->wait_resp = true;
|
||||
vdm_data->vdos[0] = vdm_hdr;
|
||||
|
||||
switch (cmd) {
|
||||
case USBPD_UVDM_CHARGER_VERSION:
|
||||
case USBPD_UVDM_CHARGER_TEMP:
|
||||
case USBPD_UVDM_CHARGER_VOLTAGE:
|
||||
vdm_data->cnt = 1;
|
||||
rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data,
|
||||
&cb_data); //&tcp_dpm_evt_cb_null
|
||||
if (rc < 0) {
|
||||
adapter_err("failed to send %d\n", cmd);
|
||||
goto release_req;
|
||||
}
|
||||
break;
|
||||
case USBPD_UVDM_VERIFIED:
|
||||
case USBPD_UVDM_REMOVE_COMPENSATION:
|
||||
vdm_data->cnt = 1 + USBPD_UVDM_VERIFIED_LEN;
|
||||
|
||||
for (i = 0; i < USBPD_UVDM_VERIFIED_LEN; i++)
|
||||
vdm_data->vdos[i + 1] = int_data[i];
|
||||
adapter_info("verify-0: %08x\n", vdm_data->vdos[1]);
|
||||
|
||||
rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data,
|
||||
&cb_data); //&tcp_dpm_evt_cb_null
|
||||
if (rc < 0) {
|
||||
adapter_err("failed to send %d\n", cmd);
|
||||
goto release_req;
|
||||
}
|
||||
break;
|
||||
case USBPD_UVDM_SESSION_SEED:
|
||||
case USBPD_UVDM_AUTHENTICATION:
|
||||
case USBPD_UVDM_REVERSE_AUTHEN:
|
||||
usbpd_sha256_bitswap32(int_data, USBPD_UVDM_SS_LEN);
|
||||
vdm_data->cnt = 1 + USBPD_UVDM_SS_LEN;
|
||||
for (i = 0; i < USBPD_UVDM_SS_LEN; i++)
|
||||
vdm_data->vdos[i + 1] = int_data[i];
|
||||
|
||||
for (i = 0; i < USBPD_UVDM_SS_LEN; i++)
|
||||
adapter_info("%08x\n", vdm_data->vdos[i + 1]);
|
||||
|
||||
rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data,
|
||||
&cb_data); //&tcp_dpm_evt_cb_null
|
||||
if (rc < 0) {
|
||||
adapter_err("failed to send %d\n", cmd);
|
||||
goto release_req;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adapter_err("cmd:%d is not support\n", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
release_req:
|
||||
if (int_data != NULL)
|
||||
kfree(int_data);
|
||||
if (vdm_data != NULL)
|
||||
kfree(vdm_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pd_get_power_role(struct adapter_device *dev)
|
||||
{
|
||||
struct xm_pd_adapter_info *info;
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
if (info == NULL || info->tcpc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
info->adapter_dev->role = tcpm_inquire_pd_power_role(info->tcpc);
|
||||
adapter_err("[%s] power role is %d\n", __func__,
|
||||
info->adapter_dev->role);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_get_current_state(struct adapter_device *dev)
|
||||
{
|
||||
struct xm_pd_adapter_info *info;
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
if (info == NULL || info->tcpc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
info->adapter_dev->current_state =
|
||||
tcpm_inquire_pd_state_curr(info->tcpc);
|
||||
adapter_err("[%s] current state is %d\n", __func__,
|
||||
info->adapter_dev->current_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_get_pdos(struct adapter_device *dev)
|
||||
{
|
||||
struct xm_pd_adapter_info *info;
|
||||
struct tcpm_power_cap cap;
|
||||
int ret, i;
|
||||
int pd_wait_cnt = 0;
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
if (info == NULL || info->tcpc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tcpm_inquire_pd_source_cap(info->tcpc, &cap);
|
||||
adapter_err("[%s] test01 tcpm_inquire_pd_source_cap is %d.\n", __func__,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
for (pd_wait_cnt = 0; pd_wait_cnt < 25; pd_wait_cnt++) {
|
||||
msleep(20);
|
||||
adapter_err(
|
||||
"retry [%s] tcpm_inquire_pd_source_cap is %d. cnt =%d\n",
|
||||
__func__, ret, pd_wait_cnt);
|
||||
ret = tcpm_inquire_pd_source_cap(info->tcpc, &cap);
|
||||
if (ret == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 7; i++) {
|
||||
info->adapter_dev->received_pdos[i] = cap.pdos[i];
|
||||
adapter_err(
|
||||
"[%s]: pdo[%d] { received_pdos is %08x, cap.pdos is %08x}\n",
|
||||
__func__, i, info->adapter_dev->received_pdos[i],
|
||||
cap.pdos[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_set_pd_verify_process(struct adapter_device *dev,
|
||||
int verify_in_process)
|
||||
{
|
||||
int ret = 0;
|
||||
//union power_supply_propval val = {0,};
|
||||
//struct power_supply *usb_psy = NULL;
|
||||
|
||||
adapter_err("[%s] pd verify in process:%d\n", __func__,
|
||||
verify_in_process);
|
||||
/*
|
||||
usb_psy = power_supply_get_by_name("usb");
|
||||
|
||||
if (usb_psy) {
|
||||
val.intval = verify_in_process;
|
||||
ret = power_supply_set_property(usb_psy,
|
||||
POWER_SUPPLY_PROP_PD_VERIFY_IN_PROCESS, &val);
|
||||
} else {
|
||||
adapter_err("[%s] usb psy not found!\n", __func__);
|
||||
}
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pd_get_cap(struct adapter_device *dev, enum adapter_cap_type type,
|
||||
struct adapter_power_cap *tacap)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int timeout = 0;
|
||||
struct xm_pd_adapter_info *info;
|
||||
struct tcpm_remote_power_cap pd_cap;
|
||||
|
||||
info = (struct xm_pd_adapter_info *)adapter_dev_get_drvdata(dev);
|
||||
|
||||
if (info == NULL || info->tcpc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (info->adapter_dev->verify_process)
|
||||
return -1;
|
||||
|
||||
if (type == XM_PD) {
|
||||
APDO_REGAIN:
|
||||
pd_cap.nr = 0;
|
||||
pd_cap.selected_cap_idx = 0;
|
||||
tcpm_get_remote_power_cap(info->tcpc, &pd_cap);
|
||||
|
||||
tacap->nr = pd_cap.nr;
|
||||
tacap->selected_cap_idx = pd_cap.selected_cap_idx - 1;
|
||||
adapter_err("[%s] nr:%d idx:%d\n", __func__, pd_cap.nr,
|
||||
pd_cap.selected_cap_idx - 1);
|
||||
for (i = 0; i < pd_cap.nr; i++) {
|
||||
tacap->ma[i] = pd_cap.ma[i];
|
||||
tacap->max_mv[i] = pd_cap.max_mv[i];
|
||||
tacap->min_mv[i] = pd_cap.min_mv[i];
|
||||
tacap->maxwatt[i] = tacap->max_mv[i] * tacap->ma[i];
|
||||
tacap->type[i] = pd_cap.type[i];
|
||||
adapter_err(
|
||||
"[%s]:%d mv:[%d,%d] %d max:%d min:%d type:%d %d\n",
|
||||
__func__, i, tacap->min_mv[i], tacap->max_mv[i],
|
||||
tacap->ma[i], tacap->maxwatt[i],
|
||||
tacap->minwatt[i], tacap->type[i],
|
||||
pd_cap.type[i]);
|
||||
}
|
||||
} else if (type == XM_PD_APDO_REGAIN) {
|
||||
get_apdo_regain = 0;
|
||||
ret = tcpm_dpm_pd_get_source_cap(info->tcpc, NULL);
|
||||
if (ret == TCPM_SUCCESS) {
|
||||
while (timeout < 10) {
|
||||
if (get_apdo_regain) {
|
||||
adapter_err(
|
||||
"[%s] ready to get pps info!\n",
|
||||
__func__);
|
||||
goto APDO_REGAIN;
|
||||
} else {
|
||||
msleep(100);
|
||||
timeout++;
|
||||
}
|
||||
}
|
||||
adapter_err("[%s] ready to get pps info - for test!\n",
|
||||
__func__);
|
||||
goto APDO_REGAIN;
|
||||
} else {
|
||||
adapter_err("[%s] tcpm_dpm_pd_get_source_cap failed!\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
adapter_err("[%s] tacap->nr is %d\n", __func__, tacap->nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adapter_ops adapter_ops = {
|
||||
.get_cap = pd_get_cap,
|
||||
.get_svid = pd_get_svid,
|
||||
.request_vdm_cmd = pd_request_vdm_cmd,
|
||||
.get_power_role = pd_get_power_role,
|
||||
.get_current_state = pd_get_current_state,
|
||||
.get_pdos = pd_get_pdos,
|
||||
.set_pd_verify_process = pd_set_pd_verify_process,
|
||||
};
|
||||
|
||||
static int adapter_parse_dt(struct xm_pd_adapter_info *info, struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
adapter_err("%s\n", __func__);
|
||||
|
||||
if (!np) {
|
||||
adapter_err("%s: no device node\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_string(np, "adapter_name",
|
||||
&info->adapter_dev_name) < 0)
|
||||
adapter_err("%s: no adapter name\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_pd_adapter_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_dev *indio_dev;
|
||||
struct xm_pd_adapter_info *info = NULL;
|
||||
static int probe_cnt = 0;
|
||||
|
||||
adapter_err("%s probe_cnt = %d\n", __func__, ++probe_cnt);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct xm_pd_adapter_info));
|
||||
if (!indio_dev) {
|
||||
adapter_err("Failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info = iio_priv(indio_dev);
|
||||
info->indio_dev = indio_dev;
|
||||
info->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
if (!g_tcpc_rt1711h || !g_battmngr) {
|
||||
adapter_err("%s: tcpc_rt1711h or g_battmngr not ready, defer\n",
|
||||
__func__);
|
||||
ret = -EPROBE_DEFER;
|
||||
msleep(100);
|
||||
if (probe_cnt >= PROBE_CNT_MAX)
|
||||
goto out;
|
||||
else
|
||||
goto err_g_tcpc_rt1711h;
|
||||
}
|
||||
|
||||
ret = rt17xx_init_iio_psy(info);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to initialize RT17XX IIO PSY, rc=%d\n", ret);
|
||||
|
||||
ret = adapter_class_init();
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to initialize adapter class, rc=%d\n", ret);
|
||||
adapter_class_exit();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adapter_check_usb_psy(info);
|
||||
adapter_check_battery_psy(info);
|
||||
|
||||
ret = adapter_parse_dt(info, &pdev->dev);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to initialize adapter parse dt, rc=%d\n", ret);
|
||||
|
||||
info->adapter_dev = adapter_device_register(
|
||||
info->adapter_dev_name, &pdev->dev, info, &adapter_ops, NULL);
|
||||
if (IS_ERR_OR_NULL(info->adapter_dev)) {
|
||||
ret = PTR_ERR(info->adapter_dev);
|
||||
goto err_register_adapter_dev;
|
||||
}
|
||||
|
||||
adapter_dev_set_drvdata(info->adapter_dev, info);
|
||||
|
||||
info->tcpc = tcpc_dev_get_by_name("type_c_port0");
|
||||
if (!info->tcpc) {
|
||||
adapter_info("%s: tcpc device not ready, defer\n", __func__);
|
||||
ret = -EPROBE_DEFER;
|
||||
msleep(100);
|
||||
if (probe_cnt >= PROBE_CNT_MAX)
|
||||
goto out;
|
||||
else
|
||||
goto err_get_tcpc_dev;
|
||||
}
|
||||
|
||||
info->pd_nb.notifier_call = pd_tcp_notifier_call;
|
||||
ret = register_tcp_dev_notifier(info->tcpc, &info->pd_nb,
|
||||
TCP_NOTIFY_TYPE_USB |
|
||||
TCP_NOTIFY_TYPE_MISC |
|
||||
TCP_NOTIFY_TYPE_MODE);
|
||||
if (ret < 0) {
|
||||
adapter_info("%s: register tcpc notifer fail\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
g_xm_pd_adapter = info;
|
||||
pr_err("%s: End!\n", __func__);
|
||||
|
||||
out:
|
||||
platform_set_drvdata(pdev, info);
|
||||
adapter_err("%s %s!!\n", __func__,
|
||||
ret == -EPROBE_DEFER ? "Over probe cnt max" : "OK");
|
||||
return 0;
|
||||
|
||||
err_register_adapter_dev:
|
||||
err_get_tcpc_dev:
|
||||
adapter_device_unregister(info->adapter_dev);
|
||||
adapter_class_exit();
|
||||
err_g_tcpc_rt1711h:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xm_pd_adapter_remove(struct platform_device *pdev)
|
||||
{
|
||||
adapter_device_unregister(g_xm_pd_adapter->adapter_dev);
|
||||
adapter_class_exit();
|
||||
devm_kfree(&pdev->dev, g_xm_pd_adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id xm_pd_adapter_of_match[] = {
|
||||
{
|
||||
.compatible = "xiaomi,pd_adapter",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, xm_pd_adapter_of_match);
|
||||
|
||||
static struct platform_driver xm_pd_adapter_driver = {
|
||||
.probe = xm_pd_adapter_probe,
|
||||
.remove = xm_pd_adapter_remove,
|
||||
.driver = {
|
||||
.name = "xm_pd_adapter",
|
||||
.of_match_table = xm_pd_adapter_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xm_pd_adapter_init(void)
|
||||
{
|
||||
return platform_driver_register(&xm_pd_adapter_driver);
|
||||
}
|
||||
module_init(xm_pd_adapter_init);
|
||||
|
||||
static void __exit xm_pd_adapter_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&xm_pd_adapter_driver);
|
||||
}
|
||||
module_exit(xm_pd_adapter_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Xiaomi PD Adapter Driver");
|
||||
MODULE_AUTHOR("getian@xiaomi.com");
|
||||
MODULE_LICENSE("GPL v2");
|
1
drivers/power/supply/xiaomi/platform/mediatek/Makefile
Normal file
1
drivers/power/supply/xiaomi/platform/mediatek/Makefile
Normal file
@ -0,0 +1 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
1
drivers/power/supply/xiaomi/platform/qualcomm/Makefile
Normal file
1
drivers/power/supply/xiaomi/platform/qualcomm/Makefile
Normal file
@ -0,0 +1 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
@ -98,8 +98,15 @@ config TYPEC_QCOM_PMIC
|
||||
It will also enable the VBUS output to connected devices when a
|
||||
DFP connection is made.
|
||||
|
||||
config XM_USB_PDPHY
|
||||
tristate "Xiaomi usb pdphy config"
|
||||
help
|
||||
Say Y or M here to enable xiaomi usb pdphy"
|
||||
|
||||
source "drivers/usb/typec/mux/Kconfig"
|
||||
|
||||
source "drivers/usb/typec/altmodes/Kconfig"
|
||||
|
||||
source "drivers/usb/typec/platform/external/tcpc/Kconfig"
|
||||
|
||||
endif # TYPEC
|
||||
|
@ -9,3 +9,4 @@ obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
|
||||
obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o
|
||||
obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
|
||||
obj-$(CONFIG_TYPEC) += mux/
|
||||
obj-$(CONFIG_XM_USB_PDPHY) += platform/
|
||||
|
5
drivers/usb/typec/platform/Makefile
Normal file
5
drivers/usb/typec/platform/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_USB_PDPHY) += external/
|
||||
obj-$(CONFIG_MTK_USB_PDPHY) += mediatek/
|
||||
obj-$(CONFIG_QCOM_USB_PDPHY) += qualcomm/
|
3
drivers/usb/typec/platform/external/Makefile
vendored
Normal file
3
drivers/usb/typec/platform/external/Makefile
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_XM_USB_PDPHY) += tcpc/
|
62
drivers/usb/typec/platform/external/tcpc/Kconfig
vendored
Normal file
62
drivers/usb/typec/platform/external/tcpc/Kconfig
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# TypeC Port Controller Device Configuration
|
||||
#
|
||||
|
||||
config TCPC_CLASS
|
||||
tristate "TypeC Port Controller Device Class"
|
||||
depends on TYPEC
|
||||
default n
|
||||
help
|
||||
Say Y to enable
|
||||
Typec Port
|
||||
Controller Device
|
||||
Class
|
||||
|
||||
config USB_POWER_DELIVERY
|
||||
tristate "Support USB power delivery Function"
|
||||
depends on TCPC_CLASS
|
||||
default n
|
||||
help
|
||||
Say Y to enable
|
||||
USB
|
||||
Power Delivery
|
||||
support
|
||||
|
||||
config DUAL_ROLE_USB_INTF
|
||||
tristate "Support DUAL ROLE USB INTF Function"
|
||||
depends on TCPC_CLASS
|
||||
default n
|
||||
help
|
||||
Say Y to enable
|
||||
Dual Role USB Intf
|
||||
support
|
||||
|
||||
config TCPC_RT1711H
|
||||
tristate "Richtek RT1711H TypeC port Controller Driver"
|
||||
depends on TCPC_CLASS
|
||||
default n
|
||||
help
|
||||
Say Y to enable
|
||||
Richtek RT1711H
|
||||
TypeC port Controller
|
||||
Driver
|
||||
|
||||
config USB_PD_VBUS_STABLE_TOUT
|
||||
int "PD VBUS Stable Timeout"
|
||||
depends on USB_POWER_DELIVERY
|
||||
range 0 1000 # >= 0, <= 1000
|
||||
default 125
|
||||
help
|
||||
Setup a timeout value (ms)
|
||||
for
|
||||
VBUS change
|
||||
stable
|
||||
|
||||
config PD_DBG_INFO
|
||||
bool "PD debug information"
|
||||
depends on TCPC_CLASS
|
||||
default y
|
||||
help
|
||||
Say Y to enable PD debug
|
||||
information
|
||||
Say N to disable
|
26
drivers/usb/typec/platform/external/tcpc/Makefile
vendored
Normal file
26
drivers/usb/typec/platform/external/tcpc/Makefile
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
subdir-ccflags-y += -Wall -Werror -DCONFIG_RT_REGMAP
|
||||
|
||||
obj-$(CONFIG_DUAL_ROLE_USB_INTF) += class_dual_role.o
|
||||
|
||||
obj-$(CONFIG_TCPC_RT1711H) += tcpc_rt1711h.o
|
||||
|
||||
obj-$(CONFIG_XM_USB_PDPHY) += tcpci_drv.o
|
||||
tcpci_drv-objs := tcpci_core.o tcpci_typec.o tcpci_timer.o tcpm.o tcpci.o \
|
||||
pd_dbg_info.o tcpci_alert.o rt-regmap.o tcpci_dual_role.o
|
||||
|
||||
ifdef CONFIG_USB_POWER_DELIVERY
|
||||
tcpci_drv-objs += tcpci_event.o \
|
||||
pd_core.o pd_policy_engine.o pd_process_evt.o \
|
||||
pd_dpm_core.o pd_dpm_uvdm.o pd_dpm_alt_mode_dp.o pd_dpm_pdo_select.o\
|
||||
pd_dpm_reaction.o \
|
||||
pd_process_evt_snk.o pd_process_evt_src.o pd_process_evt_vdm.o \
|
||||
pd_process_evt_drs.o pd_process_evt_prs.o pd_process_evt_vcs.o \
|
||||
pd_process_evt_dbg.o pd_process_evt_tcp.o pd_process_evt_com.o \
|
||||
pd_policy_engine_src.o pd_policy_engine_snk.o pd_policy_engine_ufp.o pd_policy_engine_vcs.o \
|
||||
pd_policy_engine_dfp.o pd_policy_engine_dr.o pd_policy_engine_drs.o pd_policy_engine_prs.o \
|
||||
pd_policy_engine_dbg.o pd_policy_engine_com.o pd_dpm_alt_mode_dc.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_TCPC_CLASS) += rt_pd_manager.o
|
515
drivers/usb/typec/platform/external/tcpc/class_dual_role.c
vendored
Normal file
515
drivers/usb/typec/platform/external/tcpc/class_dual_role.c
vendored
Normal file
@ -0,0 +1,515 @@
|
||||
/*
|
||||
* class-dual-role.c
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/usb/tcpc/class-dual-role.h>
|
||||
|
||||
#define DUAL_ROLE_NOTIFICATION_TIMEOUT 2000
|
||||
|
||||
static ssize_t dual_role_store_property(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t dual_role_show_property(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
#define DUAL_ROLE_ATTR(_name) \
|
||||
{ \
|
||||
.attr = { .name = #_name }, .show = dual_role_show_property, \
|
||||
.store = dual_role_store_property, \
|
||||
}
|
||||
|
||||
static struct device_attribute dual_role_attrs[] = {
|
||||
DUAL_ROLE_ATTR(supported_modes), DUAL_ROLE_ATTR(mode),
|
||||
DUAL_ROLE_ATTR(power_role), DUAL_ROLE_ATTR(data_role),
|
||||
DUAL_ROLE_ATTR(powers_vconn),
|
||||
};
|
||||
|
||||
struct class *dual_role_class;
|
||||
EXPORT_SYMBOL_GPL(dual_role_class);
|
||||
|
||||
static struct device_type dual_role_dev_type;
|
||||
|
||||
static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper)
|
||||
{
|
||||
char *ret, *ustr;
|
||||
|
||||
ustr = ret = kmalloc(strlen(str) + 1, gfp);
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
while (*str)
|
||||
*ustr++ = to_upper ? toupper(*str++) : tolower(*str++);
|
||||
|
||||
*ustr = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dual_role_changed_work(struct work_struct *work)
|
||||
{
|
||||
struct dual_role_phy_instance *dual_role =
|
||||
container_of(work, struct dual_role_phy_instance, changed_work);
|
||||
|
||||
dev_dbg(&dual_role->dev, "%s\n", __func__);
|
||||
kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
|
||||
void dual_role_instance_changed(struct dual_role_phy_instance *dual_role)
|
||||
{
|
||||
dev_dbg(&dual_role->dev, "%s\n", __func__);
|
||||
pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT);
|
||||
schedule_work(&dual_role->changed_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dual_role_instance_changed);
|
||||
|
||||
int dual_role_get_property(struct dual_role_phy_instance *dual_role,
|
||||
enum dual_role_property prop, unsigned int *val)
|
||||
{
|
||||
return dual_role->desc->get_property(dual_role, prop, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dual_role_get_property);
|
||||
|
||||
int dual_role_set_property(struct dual_role_phy_instance *dual_role,
|
||||
enum dual_role_property prop,
|
||||
const unsigned int *val)
|
||||
{
|
||||
if (!dual_role->desc->set_property)
|
||||
return -ENODEV;
|
||||
|
||||
return dual_role->desc->set_property(dual_role, prop, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dual_role_set_property);
|
||||
|
||||
int dual_role_property_is_writeable(struct dual_role_phy_instance *dual_role,
|
||||
enum dual_role_property prop)
|
||||
{
|
||||
if (!dual_role->desc->property_is_writeable)
|
||||
return -ENODEV;
|
||||
|
||||
return dual_role->desc->property_is_writeable(dual_role, prop);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dual_role_property_is_writeable);
|
||||
|
||||
static void dual_role_dev_release(struct device *dev)
|
||||
{
|
||||
struct dual_role_phy_instance *dual_role =
|
||||
container_of(dev, struct dual_role_phy_instance, dev);
|
||||
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
|
||||
kfree(dual_role);
|
||||
}
|
||||
|
||||
static struct dual_role_phy_instance *__must_check __dual_role_register(
|
||||
struct device *parent, const struct dual_role_phy_desc *desc)
|
||||
{
|
||||
struct device *dev;
|
||||
struct dual_role_phy_instance *dual_role;
|
||||
int rc;
|
||||
|
||||
dual_role = kzalloc(sizeof(*dual_role), GFP_KERNEL);
|
||||
if (!dual_role)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = &dual_role->dev;
|
||||
|
||||
device_initialize(dev);
|
||||
|
||||
dev->class = dual_role_class;
|
||||
dev->type = &dual_role_dev_type;
|
||||
dev->parent = parent;
|
||||
dev->release = dual_role_dev_release;
|
||||
dev_set_drvdata(dev, dual_role);
|
||||
dual_role->desc = desc;
|
||||
|
||||
rc = dev_set_name(dev, "%s", desc->name);
|
||||
if (rc)
|
||||
goto dev_set_name_failed;
|
||||
|
||||
INIT_WORK(&dual_role->changed_work, dual_role_changed_work);
|
||||
|
||||
rc = device_init_wakeup(dev, true);
|
||||
if (rc)
|
||||
goto wakeup_init_failed;
|
||||
|
||||
rc = device_add(dev);
|
||||
if (rc)
|
||||
goto device_add_failed;
|
||||
|
||||
dual_role_instance_changed(dual_role);
|
||||
|
||||
return dual_role;
|
||||
|
||||
device_add_failed:
|
||||
device_init_wakeup(dev, false);
|
||||
wakeup_init_failed:
|
||||
dev_set_name_failed:
|
||||
put_device(dev);
|
||||
kfree(dual_role);
|
||||
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static void
|
||||
dual_role_instance_unregister(struct dual_role_phy_instance *dual_role)
|
||||
{
|
||||
cancel_work_sync(&dual_role->changed_work);
|
||||
device_init_wakeup(&dual_role->dev, false);
|
||||
device_unregister(&dual_role->dev);
|
||||
}
|
||||
|
||||
static void devm_dual_role_release(struct device *dev, void *res)
|
||||
{
|
||||
struct dual_role_phy_instance **dual_role = res;
|
||||
|
||||
dual_role_instance_unregister(*dual_role);
|
||||
}
|
||||
|
||||
struct dual_role_phy_instance *__must_check devm_dual_role_instance_register(
|
||||
struct device *parent, const struct dual_role_phy_desc *desc)
|
||||
{
|
||||
struct dual_role_phy_instance **ptr, *dual_role;
|
||||
|
||||
ptr = devres_alloc(devm_dual_role_release, sizeof(*ptr), GFP_KERNEL);
|
||||
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
dual_role = __dual_role_register(parent, desc);
|
||||
if (IS_ERR(dual_role)) {
|
||||
devres_free(ptr);
|
||||
} else {
|
||||
*ptr = dual_role;
|
||||
devres_add(parent, ptr);
|
||||
}
|
||||
return dual_role;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_dual_role_instance_register);
|
||||
|
||||
static int devm_dual_role_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct dual_role_phy_instance **r = res;
|
||||
|
||||
if (WARN_ON(!r || !*r))
|
||||
return 0;
|
||||
|
||||
return *r == data;
|
||||
}
|
||||
|
||||
void devm_dual_role_instance_unregister(
|
||||
struct device *dev, struct dual_role_phy_instance *dual_role)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = devres_release(dev, devm_dual_role_release, devm_dual_role_match,
|
||||
dual_role);
|
||||
WARN_ON(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister);
|
||||
|
||||
void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role)
|
||||
{
|
||||
return dual_role->drv_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dual_role_get_drvdata);
|
||||
|
||||
/***************** Device attribute functions **************************/
|
||||
|
||||
/* port type */
|
||||
static char *supported_modes_text[] = { "ufp dfp", "dfp", "ufp" };
|
||||
|
||||
/* current mode */
|
||||
static char *mode_text[] = { "ufp", "dfp", "none" };
|
||||
|
||||
/* Power role */
|
||||
static char *pr_text[] = { "source", "sink", "none" };
|
||||
|
||||
/* Data role */
|
||||
static char *dr_text[] = { "host", "device", "none" };
|
||||
|
||||
/* Vconn supply */
|
||||
static char *vconn_supply_text[] = { "n", "y" };
|
||||
|
||||
static ssize_t dual_role_show_property(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
|
||||
const ptrdiff_t off = attr - dual_role_attrs;
|
||||
unsigned int value;
|
||||
|
||||
if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) {
|
||||
value = dual_role->desc->supported_modes;
|
||||
} else {
|
||||
ret = dual_role_get_property(dual_role, off, &value);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENODATA)
|
||||
dev_dbg(dev,
|
||||
"driver has no data for `%s' property\n",
|
||||
attr->attr.name);
|
||||
else if (ret != -ENODEV)
|
||||
dev_err(dev,
|
||||
"driver failed to report `%s' property: %zd\n",
|
||||
attr->attr.name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) {
|
||||
BUILD_BUG_ON(DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL !=
|
||||
ARRAY_SIZE(supported_modes_text));
|
||||
if (value < DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
supported_modes_text[value]);
|
||||
else
|
||||
return -EIO;
|
||||
} else if (off == DUAL_ROLE_PROP_MODE) {
|
||||
BUILD_BUG_ON(DUAL_ROLE_PROP_MODE_TOTAL !=
|
||||
ARRAY_SIZE(mode_text));
|
||||
if (value < DUAL_ROLE_PROP_MODE_TOTAL)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
mode_text[value]);
|
||||
else
|
||||
return -EIO;
|
||||
} else if (off == DUAL_ROLE_PROP_PR) {
|
||||
BUILD_BUG_ON(DUAL_ROLE_PROP_PR_TOTAL != ARRAY_SIZE(pr_text));
|
||||
if (value < DUAL_ROLE_PROP_PR_TOTAL)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", pr_text[value]);
|
||||
else
|
||||
return -EIO;
|
||||
} else if (off == DUAL_ROLE_PROP_DR) {
|
||||
BUILD_BUG_ON(DUAL_ROLE_PROP_DR_TOTAL != ARRAY_SIZE(dr_text));
|
||||
if (value < DUAL_ROLE_PROP_DR_TOTAL)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", dr_text[value]);
|
||||
else
|
||||
return -EIO;
|
||||
} else if (off == DUAL_ROLE_PROP_VCONN_SUPPLY) {
|
||||
BUILD_BUG_ON(DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL !=
|
||||
ARRAY_SIZE(vconn_supply_text));
|
||||
if (value < DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
vconn_supply_text[value]);
|
||||
else
|
||||
return -EIO;
|
||||
} else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static ssize_t dual_role_store_property(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
|
||||
const ptrdiff_t off = attr - dual_role_attrs;
|
||||
unsigned int value;
|
||||
int total, i;
|
||||
char *dup_buf, **text_array;
|
||||
bool result = false;
|
||||
|
||||
dup_buf = kstrdupcase(buf, GFP_KERNEL, false);
|
||||
switch (off) {
|
||||
case DUAL_ROLE_PROP_MODE:
|
||||
total = DUAL_ROLE_PROP_MODE_TOTAL;
|
||||
text_array = mode_text;
|
||||
break;
|
||||
case DUAL_ROLE_PROP_PR:
|
||||
total = DUAL_ROLE_PROP_PR_TOTAL;
|
||||
text_array = pr_text;
|
||||
break;
|
||||
case DUAL_ROLE_PROP_DR:
|
||||
total = DUAL_ROLE_PROP_DR_TOTAL;
|
||||
text_array = dr_text;
|
||||
break;
|
||||
case DUAL_ROLE_PROP_VCONN_SUPPLY:
|
||||
ret = strtobool(dup_buf, &result);
|
||||
value = result;
|
||||
if (!ret)
|
||||
goto setprop;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i <= total; i++) {
|
||||
if (i == total) {
|
||||
ret = -ENOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
if (!strncmp(*(text_array + i), dup_buf,
|
||||
strlen(*(text_array + i)))) {
|
||||
value = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setprop:
|
||||
ret = dual_role->desc->set_property(dual_role, off, &value);
|
||||
|
||||
error:
|
||||
kfree(dup_buf);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static umode_t dual_role_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int attrno)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
|
||||
umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
|
||||
int i;
|
||||
|
||||
if (attrno == DUAL_ROLE_PROP_SUPPORTED_MODES)
|
||||
return mode;
|
||||
|
||||
for (i = 0; i < dual_role->desc->num_properties; i++) {
|
||||
int property = dual_role->desc->properties[i];
|
||||
|
||||
if (property == attrno) {
|
||||
if (dual_role->desc->property_is_writeable &&
|
||||
dual_role_property_is_writeable(dual_role,
|
||||
property) > 0)
|
||||
mode |= S_IWUSR;
|
||||
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1];
|
||||
|
||||
static struct attribute_group dual_role_attr_group = {
|
||||
.attrs = __dual_role_attrs,
|
||||
.is_visible = dual_role_attr_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *dual_role_attr_groups[] = {
|
||||
&dual_role_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void dual_role_init_attrs(struct device_type *dev_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev_type->groups = dual_role_attr_groups;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dual_role_attrs); i++)
|
||||
__dual_role_attrs[i] = &dual_role_attrs[i].attr;
|
||||
}
|
||||
|
||||
int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
|
||||
int ret = 0, j;
|
||||
char *prop_buf;
|
||||
char *attrname;
|
||||
|
||||
dev_dbg(dev, "uevent\n");
|
||||
|
||||
if (!dual_role || !dual_role->desc) {
|
||||
dev_dbg(dev, "No dual_role phy yet\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "DUAL_ROLE_NAME=%s\n", dual_role->desc->name);
|
||||
|
||||
ret = add_uevent_var(env, "DUAL_ROLE_NAME=%s", dual_role->desc->name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!prop_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (j = 0; j < dual_role->desc->num_properties; j++) {
|
||||
struct device_attribute *attr;
|
||||
char *line;
|
||||
|
||||
attr = &dual_role_attrs[dual_role->desc->properties[j]];
|
||||
|
||||
ret = dual_role_show_property(dev, attr, prop_buf);
|
||||
if (ret == -ENODEV || ret == -ENODATA) {
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
line = strnchr(prop_buf, PAGE_SIZE, '\n');
|
||||
if (line)
|
||||
*line = 0;
|
||||
|
||||
attrname = kstrdupcase(attr->attr.name, GFP_KERNEL, true);
|
||||
if (!attrname)
|
||||
ret = -ENOMEM;
|
||||
|
||||
dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
|
||||
|
||||
ret = add_uevent_var(env, "DUAL_ROLE_%s=%s", attrname,
|
||||
prop_buf);
|
||||
kfree(attrname);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free_page((unsigned long)prop_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************* Module Init ***********************************/
|
||||
|
||||
static int __init dual_role_class_init(void)
|
||||
{
|
||||
dual_role_class = class_create(THIS_MODULE, "dual_role_usb");
|
||||
|
||||
if (IS_ERR(dual_role_class))
|
||||
return PTR_ERR(dual_role_class);
|
||||
|
||||
dual_role_class->dev_uevent = dual_role_uevent;
|
||||
dual_role_init_attrs(&dual_role_dev_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dual_role_class_exit(void)
|
||||
{
|
||||
class_destroy(dual_role_class);
|
||||
}
|
||||
|
||||
subsys_initcall(dual_role_class_init);
|
||||
module_exit(dual_role_class_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
|
||||
MODULE_DESCRIPTION("class-dual-role");
|
||||
MODULE_VERSION("1.0.0");
|
1482
drivers/usb/typec/platform/external/tcpc/pd_core.c
vendored
Normal file
1482
drivers/usb/typec/platform/external/tcpc/pd_core.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
168
drivers/usb/typec/platform/external/tcpc/pd_dbg_info.c
vendored
Normal file
168
drivers/usb/typec/platform/external/tcpc/pd_dbg_info.c
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/usb/tcpc/pd_dbg_info.h>
|
||||
|
||||
#ifdef CONFIG_PD_DBG_INFO
|
||||
|
||||
#define PD_INFO_BUF_SIZE (2048 * 256)
|
||||
#define MSG_POLLING_MS 20
|
||||
|
||||
#define OUT_BUF_MAX (128)
|
||||
static struct {
|
||||
int used;
|
||||
char buf[PD_INFO_BUF_SIZE + 1 + OUT_BUF_MAX];
|
||||
} pd_dbg_buffer[2];
|
||||
|
||||
static struct mutex buff_lock;
|
||||
static unsigned int using_buf;
|
||||
static wait_queue_head_t print_out_wait_que;
|
||||
static atomic_t pending_print_out;
|
||||
static atomic_t busy = ATOMIC_INIT(0);
|
||||
|
||||
void pd_dbg_info_lock(void)
|
||||
{
|
||||
atomic_inc(&busy);
|
||||
}
|
||||
|
||||
void pd_dbg_info_unlock(void)
|
||||
{
|
||||
atomic_dec_if_positive(&busy);
|
||||
}
|
||||
|
||||
static inline bool pd_dbg_print_out(void)
|
||||
{
|
||||
char temp;
|
||||
int used;
|
||||
unsigned int index, i;
|
||||
|
||||
mutex_lock(&buff_lock);
|
||||
index = using_buf;
|
||||
using_buf ^= 0x01; /* exchange buffer */
|
||||
mutex_unlock(&buff_lock);
|
||||
|
||||
used = pd_dbg_buffer[index].used;
|
||||
|
||||
if (used == 0)
|
||||
return false;
|
||||
|
||||
pd_dbg_buffer[index].buf[used] = '\0';
|
||||
|
||||
pr_info("///PD dbg info %ud\n", used);
|
||||
|
||||
for (i = 0; i < used; i += OUT_BUF_MAX) {
|
||||
temp = pd_dbg_buffer[index].buf[OUT_BUF_MAX + i];
|
||||
pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = '\0';
|
||||
|
||||
while (atomic_read(&busy))
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
pr_notice("%s", pd_dbg_buffer[index].buf + i);
|
||||
pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = temp;
|
||||
}
|
||||
|
||||
/* pr_info("PD dbg info///\n"); */
|
||||
pd_dbg_buffer[index].used = 0;
|
||||
msleep(MSG_POLLING_MS);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int print_out_thread_fn(void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (true) {
|
||||
ret = wait_event_interruptible(
|
||||
print_out_wait_que, atomic_read(&pending_print_out) ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop() || ret) {
|
||||
pr_notice("%s exits(%d)\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
do {
|
||||
atomic_dec_if_positive(&pending_print_out);
|
||||
} while (pd_dbg_print_out() && !kthread_should_stop());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pd_dbg_info(const char *fmt, ...)
|
||||
{
|
||||
unsigned int index;
|
||||
va_list args;
|
||||
int r;
|
||||
int used;
|
||||
u64 ts;
|
||||
unsigned long rem_usec;
|
||||
|
||||
ts = local_clock();
|
||||
rem_usec = do_div(ts, 1000000000) / 1000 / 1000;
|
||||
va_start(args, fmt);
|
||||
mutex_lock(&buff_lock);
|
||||
index = using_buf;
|
||||
used = pd_dbg_buffer[index].used;
|
||||
r = snprintf(pd_dbg_buffer[index].buf + used, PD_INFO_BUF_SIZE - used,
|
||||
"<%5lu.%03lu>", (unsigned long)ts, rem_usec);
|
||||
if (r > 0)
|
||||
used += r;
|
||||
r = vsnprintf(pd_dbg_buffer[index].buf + used, PD_INFO_BUF_SIZE - used,
|
||||
fmt, args);
|
||||
if (r > 0)
|
||||
used += r;
|
||||
|
||||
if (pd_dbg_buffer[index].used == 0) {
|
||||
atomic_inc(&pending_print_out);
|
||||
wake_up(&print_out_wait_que);
|
||||
}
|
||||
|
||||
pd_dbg_buffer[index].used = used;
|
||||
mutex_unlock(&buff_lock);
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(pd_dbg_info);
|
||||
|
||||
static struct task_struct *print_out_task;
|
||||
|
||||
int pd_dbg_info_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
mutex_init(&buff_lock);
|
||||
init_waitqueue_head(&print_out_wait_que);
|
||||
atomic_set(&pending_print_out, 0);
|
||||
print_out_task = kthread_run(print_out_thread_fn, NULL, "pd_dbg_info");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pd_dbg_info_exit(void)
|
||||
{
|
||||
kthread_stop(print_out_task);
|
||||
mutex_destroy(&buff_lock);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("PD Debug Info Module");
|
||||
MODULE_AUTHOR("Patrick Chang <patrick_chang@richtek.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#endif /* CONFIG_PD_DBG_INFO */
|
596
drivers/usb/typec/platform/external/tcpc/pd_dpm_alt_mode_dc.c
vendored
Normal file
596
drivers/usb/typec/platform/external/tcpc/pd_dpm_alt_mode_dc.c
vendored
Normal file
@ -0,0 +1,596 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* PD Device Policy Manager for Direct Charge
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_prv.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_POWER_DELIVERY)
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
|
||||
|
||||
#define RTDC_UVDM_EN_UNLOCK 0x2024
|
||||
#define RTDC_UVDM_RECV_EN_UNLOCK 0x4024
|
||||
#define RTDC_SVDM_PPS_AUTHORIZATION 0x10
|
||||
|
||||
#define RTDC_VALID_MODE 0x01
|
||||
#define RTDC_UVDM_EN_UNLOCK_SUCCESS 0x01
|
||||
|
||||
void crcbits(uint32_t data, uint32_t *crc, uint32_t *ppolynomial)
|
||||
{
|
||||
uint32_t i, newbit, newword, rl_crc;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
newbit = ((*crc >> 31) ^ ((data >> i) & 1)) & 1;
|
||||
if (newbit)
|
||||
newword = *ppolynomial;
|
||||
else
|
||||
newword = 0;
|
||||
rl_crc = (*crc << 1) | newbit;
|
||||
*crc = rl_crc ^ newword;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t crcwrap(uint32_t V)
|
||||
{
|
||||
uint32_t ret = 0, i, j, bit;
|
||||
|
||||
V = ~V;
|
||||
for (i = 0; i < 32; i++) {
|
||||
j = 31 - i;
|
||||
bit = (V >> i) & 1;
|
||||
ret |= bit << j;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t dc_get_random_code(void)
|
||||
{
|
||||
uint32_t num;
|
||||
|
||||
get_random_bytes(&num, sizeof(num));
|
||||
return num;
|
||||
}
|
||||
|
||||
static uint32_t dc_get_authorization_code(uint32_t data)
|
||||
{
|
||||
uint32_t dwpolynomial = 0x04C11DB6, dwCrc = 0xFFFFFFFF;
|
||||
|
||||
crcbits(data, &dwCrc, &dwpolynomial);
|
||||
dwCrc = crcwrap(dwCrc);
|
||||
return dwCrc;
|
||||
}
|
||||
|
||||
static inline bool dc_dfp_send_en_unlock(struct pd_port *pd_port, uint32_t cmd,
|
||||
uint32_t data0, uint32_t data1)
|
||||
{
|
||||
pd_port->uvdm_cnt = 3;
|
||||
pd_port->uvdm_wait_resp = true;
|
||||
|
||||
pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_DIRECTCHARGE, cmd);
|
||||
pd_port->uvdm_data[1] = data0;
|
||||
pd_port->uvdm_data[2] = data1;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
if (pd_port->pe_data.dc_pps_mode) {
|
||||
pd_port->uvdm_data[0] =
|
||||
VDO_S(USB_VID_DIRECTCHARGE, SVDM_REV20, CMDT_INIT,
|
||||
RTDC_SVDM_PPS_AUTHORIZATION, 0);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
return pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM);
|
||||
}
|
||||
|
||||
enum pd_dc_dfp_state {
|
||||
DC_DFP_NONE = 0,
|
||||
DC_DFP_DISCOVER_ID,
|
||||
DC_DFP_DISCOVER_SVIDS,
|
||||
DC_DFP_DISCOVER_MODES,
|
||||
DC_DFP_ENTER_MODE,
|
||||
DC_DFP_EN_UNLOCK1,
|
||||
DC_DFP_EN_UNLOCK2,
|
||||
DC_DFP_OPERATION,
|
||||
|
||||
#ifdef RTDC_TA_EMULATE
|
||||
DC_UFP_T0,
|
||||
DC_UFP_T1,
|
||||
DC_UFP_T2,
|
||||
#endif
|
||||
|
||||
DC_DFP_STATE_NR,
|
||||
|
||||
DC_DFP_ERR = 0X10,
|
||||
|
||||
DC_DFP_ERR_DISCOVER_ID_TYPE,
|
||||
DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_DISCOVER_SVID_DC_SID,
|
||||
DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_DISCOVER_CABLE,
|
||||
|
||||
DC_DFP_ERR_DISCOVER_MODE_DC_SID,
|
||||
DC_DFP_ERR_DISCOVER_MODE_CAP,
|
||||
DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT,
|
||||
|
||||
DC_DFP_ERR_ENTER_MODE_DC_SID,
|
||||
DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_EXIT_MODE_DC_SID,
|
||||
DC_DFP_ERR_EXIT_MODE_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_EN_UNLOCK1_FAILED,
|
||||
DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_EN_UNLOCK2_FAILED,
|
||||
DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT,
|
||||
|
||||
DC_DFP_ERR_PD_REV30,
|
||||
};
|
||||
|
||||
#if DC_DBG_ENABLE
|
||||
static const char *const dc_dfp_state_name[] = {
|
||||
"dc_dfp_none",
|
||||
"dc_dfp_discover_id",
|
||||
"dc_dfp_discover_svids",
|
||||
"dc_dfp_discover_modes",
|
||||
"dc_dfp_enter_mode",
|
||||
"dc_dfp_en_unlock1",
|
||||
"dc_dfp_en_unlock2",
|
||||
"dc_dfp_operation",
|
||||
|
||||
#ifdef RTDC_TA_EMULATE
|
||||
"dc1",
|
||||
"dc2",
|
||||
"dc3",
|
||||
#endif
|
||||
|
||||
};
|
||||
#endif /* DC_DBG_ENABLE */
|
||||
|
||||
void dc_dfp_set_state(struct pd_port *pd_port, uint8_t state)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
pd_port->dc_dfp_state = state;
|
||||
|
||||
if (pd_port->dc_dfp_state < DC_DFP_STATE_NR)
|
||||
DC_DBG("%s\n", dc_dfp_state_name[state]);
|
||||
else
|
||||
DC_DBG("dc_dfp_stop (%d)\n", state);
|
||||
}
|
||||
|
||||
bool dc_dfp_start_en_unlock1(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t rn_code[2];
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
rn_code[0] = dc_get_random_code();
|
||||
rn_code[1] = dc_get_random_code();
|
||||
pd_port->dc_pass_code = dc_get_authorization_code(
|
||||
(rn_code[0] & 0xffff) | (rn_code[1] & 0xffff0000));
|
||||
|
||||
DC_DBG("en_unlock1: 0x%x, 0x%x\n", rn_code[0], rn_code[1]);
|
||||
|
||||
dc_dfp_send_en_unlock(pd_port, RTDC_UVDM_EN_UNLOCK, rn_code[0],
|
||||
rn_code[1]);
|
||||
|
||||
dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SVDM_CMD_STATE_MASK(raw) (raw & (0x80df))
|
||||
#define SVDM_CMD_STATE(cmd, cmd_type) \
|
||||
((1 << 15) | (cmd & 0x1f) | ((cmd_type & 0x03) << 6))
|
||||
|
||||
bool dc_dfp_verify_en_unlock1(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t resp_cmd, expect_resp;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
expect_resp = RTDC_UVDM_RECV_EN_UNLOCK;
|
||||
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
if (pd_port->pe_data.dc_pps_mode) {
|
||||
resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]);
|
||||
expect_resp = SVDM_CMD_STATE(RTDC_SVDM_PPS_AUTHORIZATION,
|
||||
CMDT_RSP_ACK);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
if (resp_cmd != expect_resp) {
|
||||
DC_INFO("en_unlock1: unexpect resp (0x%x)\n", resp_cmd);
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pd_port->dc_pass_code != pd_port->uvdm_data[1]) {
|
||||
DC_INFO("en_unlock1: pass wrong 0x%x 0x%x\n",
|
||||
pd_port->dc_pass_code, pd_port->uvdm_data[1]);
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_start_en_unlock2(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t rn_code = dc_get_random_code();
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
pd_port->dc_pass_code =
|
||||
dc_get_authorization_code(pd_port->uvdm_data[2]);
|
||||
|
||||
DC_DBG("en_unlock2: 0x%x, 0x%x\n", pd_port->dc_pass_code, rn_code);
|
||||
|
||||
dc_dfp_send_en_unlock(pd_port, RTDC_UVDM_EN_UNLOCK,
|
||||
pd_port->dc_pass_code, rn_code);
|
||||
|
||||
dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_verify_en_unlock2(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t resp_cmd, expect_resp;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
expect_resp = RTDC_UVDM_RECV_EN_UNLOCK;
|
||||
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
if (pd_port->pe_data.dc_pps_mode) {
|
||||
resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]);
|
||||
expect_resp = SVDM_CMD_STATE(RTDC_SVDM_PPS_AUTHORIZATION,
|
||||
CMDT_RSP_ACK);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
if (resp_cmd != expect_resp) {
|
||||
DC_INFO("en_unlock2: unexpect resp (0x%x)\n", resp_cmd);
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pd_port->uvdm_data[1] != RTDC_UVDM_EN_UNLOCK_SUCCESS) {
|
||||
DC_INFO("en_unlock2: failed\n");
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_pe_startup(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT))
|
||||
return false;
|
||||
|
||||
if (pd_port->dpm_caps & DPM_CAP_ATTEMP_ENTER_DC_MODE)
|
||||
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_ID);
|
||||
|
||||
#ifdef RTDC_TA_EMULATE
|
||||
dc_dfp_set_state(pd_port, DC_UFP_T0);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int dc_dfp_notify_pe_ready(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
#ifdef RTDC_TA_EMULATE
|
||||
if (pd_port->data_role == PD_ROLE_DFP && svid_data->exist) {
|
||||
pd_put_tcp_pd_event(pd_port, TCP_DPM_EVT_DR_SWAP_AS_UFP);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pd_port->data_role != PD_ROLE_DFP)
|
||||
return 0;
|
||||
|
||||
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_USB_PD_RTDC_CHECK_CABLE
|
||||
if (!pd_port->pe_data.power_cable_present) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pd_get_cable_curr_lvl(pd_port) != CABLE_CURR_5A) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_RTDC_CHECK_CABLE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
/* If TA support pd revision30, using standard PPS flow */
|
||||
if (pd_check_rev30(pd_port)) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_PD_REV30);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pd_is_source_support_apdo(pd_port)) {
|
||||
DC_INFO("pps_mode\n");
|
||||
pd_port->pe_data.dc_pps_mode = true;
|
||||
return dc_dfp_start_en_unlock1(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
pd_port->mode_svid = USB_VID_DIRECTCHARGE;
|
||||
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_MODES);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_discover_id(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_ID)
|
||||
return true;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: Check device ID or Type */
|
||||
|
||||
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_SVIDS);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_discover_svid(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_SVIDS)
|
||||
return false;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!svid_data->exist) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_DC_SID);
|
||||
return false;
|
||||
}
|
||||
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_CABLE_FLOW);
|
||||
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_MODES);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_discover_modes(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES)
|
||||
return false;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port,
|
||||
DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (svid_data->remote_mode.mode_cnt == 0) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_DC_SID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (svid_data->remote_mode.mode_vdo[0] != RTDC_VALID_MODE) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_CAP);
|
||||
return false;
|
||||
}
|
||||
|
||||
pd_port->mode_obj_pos = 1;
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ENTER_MODE);
|
||||
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_ENTER_MODE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_enter_mode(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, uint8_t ops,
|
||||
bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_ENTER_MODE)
|
||||
return true;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (svid_data->active_mode == 0) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_DC_SID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return dc_dfp_start_en_unlock1(pd_port);
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_exit_mode(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, uint8_t ops)
|
||||
{
|
||||
if (pd_port->dc_dfp_state <= DC_DFP_ENTER_MODE)
|
||||
return false;
|
||||
|
||||
if (svid_data->svid != USB_VID_DIRECTCHARGE)
|
||||
return false;
|
||||
|
||||
dc_dfp_set_state(pd_port, DC_DFP_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool dc_dfp_notify_en_unlock1(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data,
|
||||
bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK1)
|
||||
return false;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dc_dfp_verify_en_unlock1(pd_port))
|
||||
return false;
|
||||
|
||||
return dc_dfp_start_en_unlock2(pd_port);
|
||||
}
|
||||
|
||||
static inline bool dc_dfp_notify_en_unlock2(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data,
|
||||
bool ack)
|
||||
{
|
||||
if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK2)
|
||||
return false;
|
||||
|
||||
if (!ack) {
|
||||
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dc_dfp_verify_en_unlock2(pd_port))
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV
|
||||
if (pd_port->pe_data.dc_pps_mode)
|
||||
pd_port->pd_revision[0] = PD_REV30;
|
||||
#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
dc_dfp_set_state(pd_port, DC_DFP_OPERATION);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
/* PPS shoult not use en_unlock to notify system */
|
||||
if (pd_port->pe_data.dc_pps_mode)
|
||||
return true;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
tcpci_dc_notify_en_unlock(pd_port->tcpc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_dfp_notify_uvdm(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, bool ack)
|
||||
{
|
||||
switch (pd_port->dc_dfp_state) {
|
||||
case DC_DFP_EN_UNLOCK1:
|
||||
dc_dfp_notify_en_unlock1(pd_port, svid_data, ack);
|
||||
break;
|
||||
|
||||
case DC_DFP_EN_UNLOCK2:
|
||||
dc_dfp_notify_en_unlock2(pd_port, svid_data, ack);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_ufp_notify_uvdm(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
#ifdef RTDC_TA_EMULATE
|
||||
uint32_t reply_cmd[3];
|
||||
uint32_t recv_code[2], rn_code, pass_code;
|
||||
|
||||
reply_cmd[0] = PD_UVDM_HDR(0x29cf, RTDC_UVDM_EN_UNLOCK);
|
||||
uint32_t cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
|
||||
|
||||
if (cmd != RTDC_UVDM_EN_UNLOCK) {
|
||||
DC_INFO("What!?");
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (pd_port->dc_dfp_state) {
|
||||
case DC_UFP_T0: {
|
||||
recv_code[0] = pd_port->uvdm_data[1];
|
||||
recv_code[1] = pd_port->uvdm_data[2];
|
||||
DC_INFO("T0: recv_code: 0x%x, 0x%x\n", recv_code[0],
|
||||
recv_code[1]);
|
||||
|
||||
pass_code = dc_get_authorization_code(
|
||||
(recv_code[0] & 0xffff) | (recv_code[1] & 0xffff0000));
|
||||
|
||||
rn_code = dc_get_random_code();
|
||||
|
||||
pd_port->dc_pass_code = dc_get_authorization_code(rn_code);
|
||||
|
||||
DC_INFO("T0: reply: 0x%x, 0x%x\n", rn_code,
|
||||
pd_port->dc_pass_code);
|
||||
|
||||
reply_cmd[1] = pass_code;
|
||||
reply_cmd[2] = rn_code;
|
||||
|
||||
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd);
|
||||
dc_dfp_set_state(pd_port, DC_UFP_T1);
|
||||
} break;
|
||||
|
||||
case DC_UFP_T1: {
|
||||
recv_code[0] = pd_port->uvdm_data[1];
|
||||
recv_code[1] = pd_port->uvdm_data[2];
|
||||
DC_INFO("T1: recv_code: 0x%x, 0x%x\n", recv_code[0],
|
||||
recv_code[1]);
|
||||
|
||||
if (recv_code[0] != pd_port->dc_pass_code) {
|
||||
DC_INFO("T1: pass_code error\n");
|
||||
reply_cmd[1] = 0;
|
||||
} else {
|
||||
DC_INFO("T1: pass_code success\n");
|
||||
reply_cmd[1] = 1;
|
||||
}
|
||||
|
||||
reply_cmd[2] = dc_get_random_code();
|
||||
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd);
|
||||
dc_dfp_set_state(pd_port, DC_UFP_T2);
|
||||
} break;
|
||||
}
|
||||
#endif /* RTDC_TA_EMULATE */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_reset_state(struct pd_port *pd_port, struct svdm_svid_data *svid_data)
|
||||
{
|
||||
dc_dfp_set_state(pd_port, DC_DFP_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dc_parse_svid_data(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
svid_data->local_mode.mode_cnt = 1;
|
||||
svid_data->local_mode.mode_vdo[0] = 0x00;
|
||||
pd_port->dpm_caps |= DPM_CAP_ATTEMP_ENTER_DC_MODE;
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
|
||||
#endif /* CONFIG_USB_POWER_DELIVERY */
|
1129
drivers/usb/typec/platform/external/tcpc/pd_dpm_alt_mode_dp.c
vendored
Normal file
1129
drivers/usb/typec/platform/external/tcpc/pd_dpm_alt_mode_dp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2327
drivers/usb/typec/platform/external/tcpc/pd_dpm_core.c
vendored
Normal file
2327
drivers/usb/typec/platform/external/tcpc/pd_dpm_core.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
389
drivers/usb/typec/platform/external/tcpc/pd_dpm_pdo_select.c
vendored
Normal file
389
drivers/usb/typec/platform/external/tcpc/pd_dpm_pdo_select.c
vendored
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Core Driver
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_dpm_pdo_select.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_POWER_DELIVERY)
|
||||
|
||||
struct dpm_select_info_t {
|
||||
uint8_t pos;
|
||||
int max_uw;
|
||||
int cur_mv;
|
||||
uint8_t policy;
|
||||
};
|
||||
|
||||
static inline void dpm_extract_apdo_info(uint32_t pdo,
|
||||
struct dpm_pdo_info_t *info)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
switch (APDO_TYPE(pdo)) {
|
||||
case APDO_TYPE_PPS:
|
||||
info->apdo_type = DPM_APDO_TYPE_PPS;
|
||||
|
||||
if (pdo & APDO_PPS_CURR_FOLDBACK)
|
||||
info->apdo_type |= DPM_APDO_TYPE_PPS_CF;
|
||||
|
||||
info->pwr_limit = APDO_PPS_EXTRACT_PWR_LIMIT(pdo);
|
||||
info->ma = APDO_PPS_EXTRACT_CURR(pdo);
|
||||
info->vmin = APDO_PPS_EXTRACT_MIN_VOLT(pdo);
|
||||
info->vmax = APDO_PPS_EXTRACT_MAX_VOLT(pdo);
|
||||
info->uw = info->ma * info->vmax;
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
info->type = TCPM_POWER_CAP_VAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
void dpm_extract_pdo_info(uint32_t pdo, struct dpm_pdo_info_t *info)
|
||||
{
|
||||
memset(info, 0, sizeof(struct dpm_pdo_info_t));
|
||||
|
||||
info->type = PDO_TYPE_VAL(pdo);
|
||||
|
||||
switch (PDO_TYPE(pdo)) {
|
||||
case PDO_TYPE_FIXED:
|
||||
info->ma = PDO_FIXED_EXTRACT_CURR(pdo);
|
||||
info->vmax = info->vmin = PDO_FIXED_EXTRACT_VOLT(pdo);
|
||||
info->uw = info->ma * info->vmax;
|
||||
break;
|
||||
case PDO_TYPE_VARIABLE:
|
||||
info->ma = PDO_VAR_EXTRACT_CURR(pdo);
|
||||
info->vmin = PDO_VAR_EXTRACT_MIN_VOLT(pdo);
|
||||
info->vmax = PDO_VAR_EXTRACT_MAX_VOLT(pdo);
|
||||
info->uw = info->ma * info->vmax;
|
||||
break;
|
||||
|
||||
case PDO_TYPE_BATTERY:
|
||||
info->uw = PDO_BATT_EXTRACT_OP_POWER(pdo) * 1000;
|
||||
info->vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
|
||||
info->vmax = PDO_BATT_EXTRACT_MAX_VOLT(pdo);
|
||||
info->ma = info->uw / info->vmin;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
case PDO_TYPE_APDO:
|
||||
dpm_extract_apdo_info(pdo, info);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a < b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
static inline int dpm_calc_src_cap_power_uw(struct dpm_pdo_info_t *source,
|
||||
struct dpm_pdo_info_t *sink)
|
||||
{
|
||||
int uw, ma;
|
||||
|
||||
if (source->type == DPM_PDO_TYPE_BAT) {
|
||||
uw = source->uw;
|
||||
|
||||
if (sink->type == DPM_PDO_TYPE_BAT)
|
||||
uw = MIN(uw, sink->uw);
|
||||
} else {
|
||||
ma = source->ma;
|
||||
|
||||
if (sink->type != DPM_PDO_TYPE_BAT)
|
||||
ma = MIN(ma, sink->ma);
|
||||
|
||||
uw = ma * source->vmax;
|
||||
}
|
||||
|
||||
return uw;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select PDO from VSafe5V
|
||||
*/
|
||||
|
||||
static bool dpm_select_pdo_from_vsafe5v(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source)
|
||||
{
|
||||
int uw;
|
||||
|
||||
if ((sink->vmax != TCPC_VBUS_SINK_5V) ||
|
||||
(sink->vmin != TCPC_VBUS_SINK_5V) ||
|
||||
(source->vmax != TCPC_VBUS_SINK_5V) ||
|
||||
(source->vmin != TCPC_VBUS_SINK_5V))
|
||||
return false;
|
||||
|
||||
uw = dpm_calc_src_cap_power_uw(source, sink);
|
||||
if (uw > select_info->max_uw) {
|
||||
select_info->max_uw = uw;
|
||||
select_info->cur_mv = source->vmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select PDO from Direct Charge
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
|
||||
static bool
|
||||
dpm_select_pdo_from_direct_charge(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source)
|
||||
{
|
||||
int uw;
|
||||
|
||||
if (sink->type != DPM_PDO_TYPE_VAR || source->type != DPM_PDO_TYPE_VAR)
|
||||
return false;
|
||||
|
||||
if (source->vmin >= TCPC_VBUS_SINK_5V)
|
||||
return false;
|
||||
|
||||
if (sink->vmax < source->vmax)
|
||||
return false;
|
||||
|
||||
if (sink->vmin > source->vmin)
|
||||
return false;
|
||||
|
||||
uw = dpm_calc_src_cap_power_uw(source, sink);
|
||||
if (uw > select_info->max_uw) {
|
||||
select_info->max_uw = uw;
|
||||
select_info->cur_mv = source->vmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
|
||||
|
||||
/*
|
||||
* Select PDO from Custom
|
||||
*/
|
||||
|
||||
static bool dpm_select_pdo_from_custom(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source)
|
||||
{
|
||||
/* TODO */
|
||||
return dpm_select_pdo_from_vsafe5v(select_info, sink, source);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select PDO from Max Power
|
||||
*/
|
||||
|
||||
static inline bool dpm_is_valid_pdo_pair(struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source,
|
||||
uint32_t policy)
|
||||
{
|
||||
if (sink->vmax < source->vmax)
|
||||
return false;
|
||||
|
||||
if (sink->vmin > source->vmin)
|
||||
return false;
|
||||
|
||||
if (policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR)
|
||||
return true;
|
||||
|
||||
return sink->ma <= source->ma;
|
||||
}
|
||||
|
||||
static bool dpm_select_pdo_from_max_power(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source)
|
||||
{
|
||||
bool overload;
|
||||
int uw;
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
|
||||
/* Variable for direct charge only */
|
||||
if ((sink->type == DPM_PDO_TYPE_VAR) && (sink->vmin < 5000))
|
||||
return false;
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
if (sink->type == DPM_PDO_TYPE_APDO)
|
||||
return false;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
if (!dpm_is_valid_pdo_pair(sink, source, select_info->policy))
|
||||
return false;
|
||||
|
||||
uw = dpm_calc_src_cap_power_uw(source, sink);
|
||||
|
||||
overload = uw > select_info->max_uw;
|
||||
|
||||
if ((!overload) && (uw == select_info->max_uw)) {
|
||||
if (select_info->policy &
|
||||
DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE)
|
||||
overload = (source->vmax < select_info->cur_mv);
|
||||
else if (select_info->policy &
|
||||
DPM_CHARGING_POLICY_PREFER_HIGH_VOLTAGE)
|
||||
overload = (source->vmax > select_info->cur_mv);
|
||||
}
|
||||
|
||||
if (overload) {
|
||||
select_info->max_uw = uw;
|
||||
select_info->cur_mv = source->vmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select PDO from PPS
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
static bool dpm_select_pdo_from_pps(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source)
|
||||
{
|
||||
bool overload;
|
||||
int uw, diff_mv;
|
||||
const int tolerance = 300; /* 5900 * 5% */
|
||||
|
||||
if (sink->type != DPM_PDO_TYPE_APDO ||
|
||||
source->type != DPM_PDO_TYPE_APDO)
|
||||
return false;
|
||||
|
||||
if (!(source->apdo_type & DPM_APDO_TYPE_PPS))
|
||||
return false;
|
||||
|
||||
if (sink->vmax > source->vmax)
|
||||
return false;
|
||||
|
||||
if (sink->vmin < source->vmin)
|
||||
return false;
|
||||
|
||||
if (!(select_info->policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR)) {
|
||||
if (source->ma < sink->ma)
|
||||
return false;
|
||||
}
|
||||
|
||||
uw = sink->vmax * source->ma;
|
||||
diff_mv = source->vmax - sink->vmax;
|
||||
|
||||
if (uw > select_info->max_uw)
|
||||
overload = true;
|
||||
else if (uw < select_info->max_uw)
|
||||
overload = false;
|
||||
else if ((select_info->cur_mv < tolerance) && (diff_mv > tolerance))
|
||||
overload = true;
|
||||
else if (diff_mv < select_info->cur_mv)
|
||||
overload = true;
|
||||
else
|
||||
overload = false;
|
||||
|
||||
if (overload) {
|
||||
select_info->max_uw = uw;
|
||||
select_info->cur_mv = diff_mv;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
/*
|
||||
* Select PDO from defined rule ...
|
||||
*/
|
||||
|
||||
typedef bool (*dpm_select_pdo_fun)(struct dpm_select_info_t *select_info,
|
||||
struct dpm_pdo_info_t *sink,
|
||||
struct dpm_pdo_info_t *source);
|
||||
|
||||
bool dpm_find_match_req_info(struct dpm_rdo_info_t *req_info,
|
||||
struct dpm_pdo_info_t *sink, int cnt,
|
||||
uint32_t *src_pdos, int min_uw, uint32_t policy)
|
||||
{
|
||||
int i;
|
||||
struct dpm_select_info_t select;
|
||||
struct dpm_pdo_info_t source;
|
||||
dpm_select_pdo_fun select_pdo_fun;
|
||||
|
||||
select.pos = 0;
|
||||
select.cur_mv = 0;
|
||||
select.max_uw = min_uw;
|
||||
select.policy = policy;
|
||||
|
||||
switch (policy & DPM_CHARGING_POLICY_MASK) {
|
||||
case DPM_CHARGING_POLICY_MAX_POWER:
|
||||
select_pdo_fun = dpm_select_pdo_from_max_power;
|
||||
break;
|
||||
|
||||
case DPM_CHARGING_POLICY_CUSTOM:
|
||||
select_pdo_fun = dpm_select_pdo_from_custom;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
|
||||
case DPM_CHARGING_POLICY_DIRECT_CHARGE:
|
||||
select_pdo_fun = dpm_select_pdo_from_direct_charge;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
case DPM_CHARGING_POLICY_PPS:
|
||||
select_pdo_fun = dpm_select_pdo_from_pps;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
default: /* DPM_CHARGING_POLICY_VSAFE5V */
|
||||
select_pdo_fun = dpm_select_pdo_from_vsafe5v;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
dpm_extract_pdo_info(src_pdos[i], &source);
|
||||
|
||||
if (select_pdo_fun(&select, sink, &source))
|
||||
select.pos = i + 1;
|
||||
}
|
||||
|
||||
if (select.pos > 0) {
|
||||
dpm_extract_pdo_info(src_pdos[select.pos - 1], &source);
|
||||
|
||||
req_info->pos = select.pos;
|
||||
req_info->type = source.type;
|
||||
req_info->vmax = source.vmax;
|
||||
req_info->vmin = source.vmin;
|
||||
|
||||
if (sink->type == DPM_PDO_TYPE_BAT)
|
||||
req_info->mismatch = select.max_uw < sink->uw;
|
||||
else
|
||||
req_info->mismatch = source.ma < sink->ma;
|
||||
|
||||
if (source.type == DPM_PDO_TYPE_BAT) {
|
||||
req_info->max_uw = sink->uw;
|
||||
req_info->oper_uw = select.max_uw;
|
||||
} else {
|
||||
req_info->max_ma = sink->ma;
|
||||
req_info->oper_ma = MIN(sink->ma, source.ma);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
if (source.type == DPM_PDO_TYPE_APDO) {
|
||||
req_info->vmax = sink->vmax;
|
||||
req_info->vmin = sink->vmin;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_POWER_DELIVERY */
|
736
drivers/usb/typec/platform/external/tcpc/pd_dpm_reaction.c
vendored
Normal file
736
drivers/usb/typec/platform/external/tcpc/pd_dpm_reaction.c
vendored
Normal file
@ -0,0 +1,736 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* PD Device Policy Manager Ready State reactions
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_pdo_select.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_prv.h>
|
||||
|
||||
#define DPM_REACTION_COND_ALWAYS (1 << 0)
|
||||
#define DPM_REACTION_COND_UFP_ONLY (1 << 1)
|
||||
#define DPM_REACTION_COND_DFP_ONLY (1 << 2)
|
||||
#define DPM_REACTION_COND_PD30 (1 << 3)
|
||||
|
||||
#define DPM_REACCOND_DFP (DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_DFP_ONLY)
|
||||
|
||||
#define DPM_REACCOND_UFP (DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_UFP_ONLY)
|
||||
|
||||
#define DPM_REACTION_COND_CHECK_ONCE (1 << 5)
|
||||
#define DPM_REACTION_COND_ONE_SHOT (1 << 6)
|
||||
#define DPM_REACTION_COND_LIMITED_RETRIES (1 << 7)
|
||||
|
||||
/*
|
||||
* DPM flow delay reactions
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
|
||||
static uint8_t dpm_reaction_ufp_flow_delay(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
DPM_INFO("UFP Delay\n");
|
||||
pd_restart_timer(pd_port, PD_TIMER_UFP_FLOW_DELAY);
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
|
||||
static uint8_t dpm_reaction_dfp_flow_delay(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
DPM_INFO("DFP Delay\n");
|
||||
pd_restart_timer(pd_port, PD_TIMER_DFP_FLOW_DELAY);
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
|
||||
static uint8_t dpm_reaction_vconn_stable_delay(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) {
|
||||
DPM_INFO("VStable Delay\n");
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
|
||||
|
||||
/*
|
||||
* DPM get cap reaction
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static uint8_t dpm_reaction_get_source_cap_ext(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->power_role == PD_ROLE_SINK)
|
||||
return TCP_DPM_EVT_GET_SOURCE_CAP_EXT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
static uint8_t dpm_reaction_get_sink_cap(struct pd_port *pd_port)
|
||||
{
|
||||
return TCP_DPM_EVT_GET_SINK_CAP;
|
||||
}
|
||||
|
||||
static uint8_t dpm_reaction_get_source_cap(struct pd_port *pd_port)
|
||||
{
|
||||
return TCP_DPM_EVT_GET_SOURCE_CAP;
|
||||
}
|
||||
|
||||
static uint8_t dpm_reaction_attemp_get_flag(struct pd_port *pd_port)
|
||||
{
|
||||
return TCP_DPM_EVT_GET_SINK_CAP;
|
||||
}
|
||||
|
||||
/*
|
||||
* DPM swap reaction
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
static uint8_t dpm_reaction_request_pr_swap(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t prefer_role = DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps);
|
||||
|
||||
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER))
|
||||
return 0;
|
||||
|
||||
if (pd_port->power_role == PD_ROLE_SINK) {
|
||||
if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SRC)
|
||||
return TCP_DPM_EVT_PR_SWAP_AS_SRC;
|
||||
} else {
|
||||
#ifdef CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW
|
||||
if (dpm_check_good_power(pd_port) == GOOD_PW_PARTNER)
|
||||
return TCP_DPM_EVT_PR_SWAP_AS_SNK;
|
||||
#endif /* CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW */
|
||||
|
||||
if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SNK)
|
||||
return TCP_DPM_EVT_PR_SWAP_AS_SNK;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
static uint8_t dpm_reaction_request_dr_swap(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t prefer_role = DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps);
|
||||
|
||||
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA))
|
||||
return 0;
|
||||
|
||||
if (pd_port->data_role == PD_ROLE_DFP &&
|
||||
prefer_role == DPM_CAP_DR_CHECK_PREFER_UFP)
|
||||
return TCP_DPM_EVT_DR_SWAP_AS_UFP;
|
||||
|
||||
if (pd_port->data_role == PD_ROLE_UFP &&
|
||||
prefer_role == DPM_CAP_DR_CHECK_PREFER_DFP)
|
||||
return TCP_DPM_EVT_DR_SWAP_AS_DFP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* #ifdef CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
/*
|
||||
* DPM DiscoverCable reaction
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
|
||||
static uint8_t dpm_reaction_dynamic_vconn(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dynamic_enable_vconn(pd_port);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN
|
||||
static uint8_t dpm_reaction_request_vconn_source(struct pd_port *pd_port)
|
||||
{
|
||||
bool return_vconn = true;
|
||||
|
||||
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY))
|
||||
return 0;
|
||||
|
||||
if (pd_port->vconn_role)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
|
||||
if (pd_port->tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_STARTUP)
|
||||
return_vconn = false;
|
||||
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
|
||||
|
||||
if (pd_check_rev30(pd_port))
|
||||
return_vconn = false;
|
||||
|
||||
if (return_vconn)
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_RETURN_VCONN_SRC);
|
||||
|
||||
return TCP_DPM_EVT_VCONN_SWAP_ON;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
|
||||
static uint8_t pd_dpm_reaction_discover_cable(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
if (pd_is_reset_cable(pd_port))
|
||||
return TCP_DPM_EVT_CABLE_SOFTRESET;
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
|
||||
if (pd_is_discover_cable(pd_port)) {
|
||||
pd_restart_timer(pd_port, PD_TIMER_DISCOVER_ID);
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN
|
||||
static uint8_t dpm_reaction_return_vconn_source(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->vconn_role) {
|
||||
DPM_DBG("VconnReturn\n");
|
||||
return TCP_DPM_EVT_VCONN_SWAP_OFF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */
|
||||
|
||||
/*
|
||||
* DPM EnterMode reaction
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID
|
||||
static uint8_t dpm_reaction_discover_id(struct pd_port *pd_port)
|
||||
{
|
||||
return TCP_DPM_EVT_DISCOVER_ID;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
|
||||
static uint8_t dpm_reaction_discover_svid(struct pd_port *pd_port)
|
||||
{
|
||||
return TCP_DPM_EVT_DISCOVER_SVIDS;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_MODE_OPERATION
|
||||
static uint8_t dpm_reaction_mode_operation(struct pd_port *pd_port)
|
||||
{
|
||||
if (svdm_notify_pe_ready(pd_port))
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_MODE_OPERATION */
|
||||
|
||||
/*
|
||||
* DPM Local/Remote Alert reaction
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT
|
||||
|
||||
static uint8_t dpm_reaction_send_alert(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t alert_urgent;
|
||||
struct pe_data *pe_data = &pd_port->pe_data;
|
||||
|
||||
alert_urgent = pe_data->local_alert;
|
||||
alert_urgent &= ~ADO_GET_STATUS_ONCE_MASK;
|
||||
|
||||
if (!pe_data->get_status_once)
|
||||
pe_data->local_alert = alert_urgent;
|
||||
|
||||
if ((!pe_data->pe_ready) && (alert_urgent == 0))
|
||||
return 0;
|
||||
|
||||
if (pe_data->local_alert == 0)
|
||||
return 0;
|
||||
|
||||
return TCP_DPM_EVT_ALERT;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS
|
||||
|
||||
const uint32_t c_get_status_alert_type =
|
||||
ADO_ALERT_OCP | ADO_ALERT_OTP | ADO_ALERT_OVP | ADO_ALERT_OPER_CHANGED |
|
||||
ADO_ALERT_SRC_IN_CHANGED;
|
||||
|
||||
static inline uint8_t dpm_reaction_alert_status_changed(struct pd_port *pd_port)
|
||||
{
|
||||
pd_port->pe_data.remote_alert &=
|
||||
~ADO_ALERT_TYPE_SET(c_get_status_alert_type);
|
||||
|
||||
return TCP_DPM_EVT_GET_STATUS;
|
||||
}
|
||||
|
||||
static inline uint8_t dpm_reaction_alert_battry_changed(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t mask;
|
||||
uint8_t bat_change_i = 255;
|
||||
uint8_t bat_change_mask1, bat_change_mask2;
|
||||
|
||||
bat_change_mask1 = ADO_FIXED_BAT(pd_port->pe_data.remote_alert);
|
||||
bat_change_mask2 = ADO_HOT_SWAP_BAT(pd_port->pe_data.remote_alert);
|
||||
|
||||
if (bat_change_mask1) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
mask = 1 << i;
|
||||
if (bat_change_mask1 & mask) {
|
||||
bat_change_i = i;
|
||||
bat_change_mask1 &= ~mask;
|
||||
pd_port->pe_data.remote_alert &=
|
||||
~ADO_FIXED_BAT_SET(bat_change_mask1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (bat_change_mask2) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
mask = 1 << i;
|
||||
if (bat_change_mask2 & mask) {
|
||||
bat_change_i = i + 4;
|
||||
bat_change_mask2 &= ~mask;
|
||||
pd_port->pe_data.remote_alert &=
|
||||
~ADO_HOT_SWAP_BAT_SET(bat_change_mask2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bat_change_mask1 == 0 && bat_change_mask2 == 0) {
|
||||
pd_port->pe_data.remote_alert &=
|
||||
~ADO_ALERT_TYPE_SET(ADO_ALERT_BAT_CHANGED);
|
||||
}
|
||||
|
||||
if (bat_change_i == 255)
|
||||
return 0;
|
||||
|
||||
pd_port->tcp_event.tcp_dpm_data.data_object[0] = bat_change_i;
|
||||
return TCP_DPM_EVT_GET_BAT_STATUS;
|
||||
}
|
||||
|
||||
static uint8_t dpm_reaction_handle_alert(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t alert_type = ADO_ALERT_TYPE(pd_port->pe_data.remote_alert);
|
||||
|
||||
if (alert_type & c_get_status_alert_type)
|
||||
return dpm_reaction_alert_status_changed(pd_port);
|
||||
|
||||
if (alert_type & ADO_ALERT_BAT_CHANGED)
|
||||
return dpm_reaction_alert_battry_changed(pd_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
/*
|
||||
* DPM Idle reaction
|
||||
*/
|
||||
|
||||
static inline uint8_t dpm_get_pd_connect_state(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->power_role == PD_ROLE_SOURCE) {
|
||||
if (pd_check_rev30(pd_port))
|
||||
return PD_CONNECT_PE_READY_SRC_PD30;
|
||||
|
||||
return PD_CONNECT_PE_READY_SRC;
|
||||
}
|
||||
|
||||
if (pd_check_rev30(pd_port)) {
|
||||
if (pd_is_source_support_apdo(pd_port))
|
||||
return PD_CONNECT_PE_READY_SNK_APDO;
|
||||
|
||||
return PD_CONNECT_PE_READY_SNK_PD30;
|
||||
}
|
||||
|
||||
return PD_CONNECT_PE_READY_SNK;
|
||||
}
|
||||
|
||||
static inline void dpm_check_vconn_highv_prot(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY
|
||||
struct tcpc_device *tcpc = pd_port->tcpc;
|
||||
struct pe_data *pe_data = &pd_port->pe_data;
|
||||
bool vconn_highv_prot = pd_port->request_v_new > 5000;
|
||||
|
||||
if (pe_data->vconn_highv_prot && !vconn_highv_prot &&
|
||||
tcpc->tcpc_flags & TCPC_FLAGS_VCONN_SAFE5V_ONLY) {
|
||||
DPM_INFO("VC_HIGHV_PROT: %d\n", vconn_highv_prot);
|
||||
pe_data->vconn_highv_prot = vconn_highv_prot;
|
||||
pd_set_vconn(pd_port, pe_data->vconn_highv_prot_role);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */
|
||||
}
|
||||
|
||||
static uint8_t dpm_reaction_update_pe_ready(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t state;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (!pd_port->pe_data.pe_ready) {
|
||||
DPM_INFO("PE_READY\n");
|
||||
pd_port->pe_data.pe_ready = true;
|
||||
#if IS_ENABLED(CONFIG_DUAL_ROLE_USB_INTF)
|
||||
dual_role_instance_changed(pd_port->tcpc->dr_usb);
|
||||
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
|
||||
}
|
||||
|
||||
state = dpm_get_pd_connect_state(pd_port);
|
||||
pd_update_connect_state(pd_port, state);
|
||||
|
||||
dpm_check_vconn_highv_prot(pd_port);
|
||||
pd_dpm_dynamic_disable_vconn(pd_port);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
pd_port->pe_data.pd_traffic_idle = true;
|
||||
if (pd_check_rev30(pd_port) && (pd_port->power_role == PD_ROLE_SOURCE))
|
||||
pd_set_sink_tx(pd_port, PD30_SINK_TX_OK);
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DPM reaction declaration
|
||||
*/
|
||||
|
||||
typedef uint8_t (*dpm_reaction_fun)(struct pd_port *pd_port);
|
||||
|
||||
struct dpm_ready_reaction {
|
||||
uint32_t bit_mask;
|
||||
uint8_t condition;
|
||||
dpm_reaction_fun handler;
|
||||
};
|
||||
|
||||
#define DECL_DPM_REACTION(xmask, xcond, xhandler) \
|
||||
{ \
|
||||
.bit_mask = xmask, .condition = xcond, .handler = xhandler, \
|
||||
}
|
||||
|
||||
#define DECL_DPM_REACTION_ALWAYS(xhandler) \
|
||||
DECL_DPM_REACTION(DPM_REACTION_CAP_ALWAYS, DPM_REACTION_COND_ALWAYS, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_CHECK_ONCE(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_ALWAYS | \
|
||||
DPM_REACTION_COND_CHECK_ONCE, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_LIMITED_RETRIES(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_ALWAYS | \
|
||||
DPM_REACTION_COND_LIMITED_RETRIES, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_ONE_SHOT(xmask, xhandler) \
|
||||
DECL_DPM_REACTION( \
|
||||
xmask, DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_ONE_SHOT, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_UFP(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, DPM_REACTION_COND_UFP_ONLY, xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_DFP(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, DPM_REACTION_COND_DFP_ONLY, xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_PD30(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, DPM_REACTION_COND_PD30, xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_PD30_LIMITED_RETRIES(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_PD30 | \
|
||||
DPM_REACTION_COND_LIMITED_RETRIES, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_PD30_ONE_SHOT(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_PD30 | DPM_REACTION_COND_ONE_SHOT, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_DFP_PD30_ONE_SHOT(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_DFP_ONLY | \
|
||||
DPM_REACTION_COND_PD30 | \
|
||||
DPM_REACTION_COND_ONE_SHOT, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_DFP_ONLY | \
|
||||
DPM_REACTION_COND_PD30 | \
|
||||
DPM_REACTION_COND_LIMITED_RETRIES, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_DFP_ONLY | \
|
||||
DPM_REACTION_COND_PD30 | \
|
||||
DPM_REACTION_COND_CHECK_ONCE, \
|
||||
xhandler)
|
||||
|
||||
#define DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(xmask, xhandler) \
|
||||
DECL_DPM_REACTION(xmask, \
|
||||
DPM_REACTION_COND_DFP_ONLY | \
|
||||
DPM_REACTION_COND_PD30 | \
|
||||
DPM_REACTION_COND_CHECK_ONCE | \
|
||||
DPM_REACTION_COND_ONE_SHOT, \
|
||||
xhandler)
|
||||
|
||||
static const struct dpm_ready_reaction dpm_reactions[] = {
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT
|
||||
DECL_DPM_REACTION_PD30(DPM_REACTION_CAP_ALWAYS,
|
||||
dpm_reaction_send_alert),
|
||||
#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */
|
||||
#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS
|
||||
DECL_DPM_REACTION_PD30(DPM_REACTION_CAP_ALWAYS,
|
||||
dpm_reaction_handle_alert),
|
||||
#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
|
||||
DECL_DPM_REACTION_DFP(DPM_REACTION_DFP_FLOW_DELAY,
|
||||
dpm_reaction_dfp_flow_delay),
|
||||
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
|
||||
DECL_DPM_REACTION_UFP(DPM_REACTION_UFP_FLOW_DELAY,
|
||||
dpm_reaction_ufp_flow_delay),
|
||||
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
|
||||
|
||||
DECL_DPM_REACTION_LIMITED_RETRIES(DPM_REACTION_GET_SINK_CAP,
|
||||
dpm_reaction_get_sink_cap),
|
||||
|
||||
DECL_DPM_REACTION_LIMITED_RETRIES(DPM_REACTION_GET_SOURCE_CAP,
|
||||
dpm_reaction_get_source_cap),
|
||||
|
||||
DECL_DPM_REACTION_LIMITED_RETRIES(DPM_REACTION_ATTEMPT_GET_FLAG,
|
||||
dpm_reaction_attemp_get_flag),
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
DECL_DPM_REACTION_PD30_ONE_SHOT(DPM_REACTION_GET_SOURCE_CAP_EXT,
|
||||
dpm_reaction_get_source_cap_ext),
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
DECL_DPM_REACTION_CHECK_ONCE(DPM_REACTION_REQUEST_PR_SWAP,
|
||||
dpm_reaction_request_pr_swap),
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
DECL_DPM_REACTION_CHECK_ONCE(DPM_REACTION_REQUEST_DR_SWAP,
|
||||
dpm_reaction_request_dr_swap),
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
|
||||
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(DPM_REACTION_DYNAMIC_VCONN,
|
||||
dpm_reaction_dynamic_vconn),
|
||||
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN
|
||||
DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(DPM_REACTION_REQUEST_VCONN_SRC,
|
||||
dpm_reaction_request_vconn_source),
|
||||
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
|
||||
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(DPM_REACTION_VCONN_STABLE_DELAY,
|
||||
dpm_reaction_vconn_stable_delay),
|
||||
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
|
||||
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(DPM_REACTION_DISCOVER_CABLE,
|
||||
pd_dpm_reaction_discover_cable),
|
||||
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN
|
||||
DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(DPM_REACTION_RETURN_VCONN_SRC,
|
||||
dpm_reaction_return_vconn_source),
|
||||
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */
|
||||
|
||||
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID
|
||||
DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(DPM_REACTION_DISCOVER_ID,
|
||||
dpm_reaction_discover_id),
|
||||
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
|
||||
DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(DPM_REACTION_DISCOVER_SVID,
|
||||
dpm_reaction_discover_svid),
|
||||
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_MODE_OPERATION
|
||||
DECL_DPM_REACTION_ALWAYS(dpm_reaction_mode_operation),
|
||||
#endif /* CONFIG_USB_PD_MODE_OPERATION */
|
||||
|
||||
DECL_DPM_REACTION_ALWAYS(dpm_reaction_update_pe_ready),
|
||||
};
|
||||
|
||||
/**
|
||||
* dpm_get_reaction_env
|
||||
*
|
||||
* Get current reaction's environmental conditions.
|
||||
*
|
||||
* Returns environmental conditions.
|
||||
*/
|
||||
|
||||
static inline uint8_t dpm_get_reaction_env(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t conditions;
|
||||
|
||||
if (pd_port->data_role == PD_ROLE_DFP)
|
||||
conditions = DPM_REACCOND_DFP;
|
||||
else
|
||||
conditions = DPM_REACCOND_UFP;
|
||||
|
||||
if (pd_check_rev30(pd_port))
|
||||
conditions |= DPM_REACTION_COND_PD30;
|
||||
|
||||
return conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_check_reaction_busy
|
||||
*
|
||||
* check this reaction is still keep busy
|
||||
*
|
||||
* @ reaction : which reaction is checked.
|
||||
*
|
||||
* Return Boolean to indicate busy or not.
|
||||
*/
|
||||
|
||||
static inline bool
|
||||
dpm_check_reaction_busy(struct pd_port *pd_port,
|
||||
const struct dpm_ready_reaction *reaction)
|
||||
{
|
||||
if (reaction->bit_mask & DPM_REACTION_CAP_ALWAYS)
|
||||
return false;
|
||||
|
||||
return !dpm_reaction_check(pd_port, DPM_REACTION_CAP_READY_ONCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_check_reaction_available
|
||||
*
|
||||
* check this reaction is available.
|
||||
*
|
||||
* @ env : Environmental conditions of reaction table
|
||||
* @ reaction : which reaction is checked.
|
||||
*
|
||||
* Returns TCP_DPM_EVT_ID if available;
|
||||
* Returns Zero to indicate if not available.
|
||||
* Returns DPM_READY_REACTION_BUSY to indicate
|
||||
* this reaction is waiting for being finished.
|
||||
*/
|
||||
|
||||
static inline uint8_t
|
||||
dpm_check_reaction_available(struct pd_port *pd_port, uint8_t env,
|
||||
const struct dpm_ready_reaction *reaction)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dpm_reaction_check(pd_port, reaction->bit_mask))
|
||||
return 0;
|
||||
|
||||
if (!(reaction->condition & env))
|
||||
return 0;
|
||||
|
||||
if (dpm_check_reaction_busy(pd_port, reaction))
|
||||
return DPM_READY_REACTION_BUSY;
|
||||
|
||||
ret = reaction->handler(pd_port);
|
||||
|
||||
if (ret == 0 && reaction->condition & DPM_REACTION_COND_CHECK_ONCE)
|
||||
dpm_reaction_clear(pd_port, reaction->bit_mask);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_check_reset_reaction
|
||||
*
|
||||
* Once trigger one reaction,
|
||||
* check if automatically clear this reaction flag.
|
||||
*
|
||||
* For the following reactions type:
|
||||
* DPM_REACTION_COND_ONE_SHOT,
|
||||
* DPM_REACTION_COND_LIMITED_RETRIES
|
||||
*
|
||||
* @ reaction : which reaction is triggered.
|
||||
*
|
||||
* Return Boolean to indicate automatically clear or not.
|
||||
*/
|
||||
|
||||
static inline bool
|
||||
dpm_check_clear_reaction(struct pd_port *pd_port,
|
||||
const struct dpm_ready_reaction *reaction)
|
||||
{
|
||||
if (pd_port->pe_data.dpm_reaction_id != reaction->bit_mask)
|
||||
pd_port->pe_data.dpm_reaction_retry = 0;
|
||||
|
||||
pd_port->pe_data.dpm_reaction_retry++;
|
||||
pd_port->pe_data.dpm_reaction_id = reaction->bit_mask;
|
||||
|
||||
if (reaction->condition & DPM_REACTION_COND_ONE_SHOT)
|
||||
return true;
|
||||
|
||||
if (reaction->condition & DPM_REACTION_COND_LIMITED_RETRIES)
|
||||
return pd_port->pe_data.dpm_reaction_retry > 3;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t pd_dpm_get_ready_reaction(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t evt;
|
||||
uint8_t env;
|
||||
uint32_t clear_reaction = DPM_REACTION_CAP_READY_ONCE;
|
||||
const struct dpm_ready_reaction *reaction = dpm_reactions;
|
||||
const struct dpm_ready_reaction *reaction_last =
|
||||
dpm_reactions + ARRAY_SIZE(dpm_reactions);
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
env = dpm_get_reaction_env(pd_port);
|
||||
|
||||
do {
|
||||
evt = dpm_check_reaction_available(pd_port, env, reaction);
|
||||
} while ((evt == 0) && (++reaction < reaction_last));
|
||||
|
||||
if (evt > 0 && dpm_check_clear_reaction(pd_port, reaction)) {
|
||||
clear_reaction |= reaction->bit_mask;
|
||||
DPM_DBG("clear_reaction=%d\n", evt);
|
||||
}
|
||||
|
||||
dpm_reaction_clear(pd_port, clear_reaction);
|
||||
return evt;
|
||||
}
|
98
drivers/usb/typec/platform/external/tcpc/pd_dpm_uvdm.c
vendored
Normal file
98
drivers/usb/typec/platform/external/tcpc/pd_dpm_uvdm.c
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* PD Device Policy Manager for UVDM
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_prv.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_RICHTEK_UVDM
|
||||
|
||||
bool richtek_dfp_notify_pe_startup(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
UVDM_INFO("%s\n", __func__);
|
||||
pd_port->richtek_init_done = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int richtek_dfp_notify_pe_ready(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
if (pd_port->data_role != PD_ROLE_DFP)
|
||||
return 0;
|
||||
|
||||
if (pd_port->richtek_init_done)
|
||||
return 0;
|
||||
|
||||
pd_port->richtek_init_done = true;
|
||||
UVDM_INFO("%s\n", __func__);
|
||||
|
||||
#if 0
|
||||
pd_port->uvdm_cnt = 3;
|
||||
pd_port->uvdm_wait_resp = true;
|
||||
|
||||
pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_RICHTEK, 0x4321);
|
||||
pd_port->uvdm_data[1] = 0x11223344;
|
||||
pd_port->uvdm_data[2] = 0x44332211;
|
||||
|
||||
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool richtek_dfp_notify_uvdm(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data, bool ack)
|
||||
{
|
||||
uint32_t resp_cmd = 0;
|
||||
|
||||
if (ack) {
|
||||
if (pd_port->uvdm_wait_resp)
|
||||
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
|
||||
|
||||
UVDM_INFO("dfp_notify: ACK (0x%x)\n", resp_cmd);
|
||||
} else
|
||||
UVDM_INFO("dfp_notify: NAK\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool richtek_ufp_notify_uvdm(struct pd_port *pd_port,
|
||||
struct svdm_svid_data *svid_data)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t reply_cmd[VDO_MAX_NR];
|
||||
uint16_t cmd = (uint16_t)PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
|
||||
|
||||
UVDM_INFO("ufp_notify: 0x%x\n", cmd);
|
||||
|
||||
if (cmd >= 0x1000) {
|
||||
UVDM_INFO("uvdm_no_reply\n");
|
||||
VDM_STATE_DPM_INFORMED(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
reply_cmd[0] = PD_UVDM_HDR(USB_VID_RICHTEK, cmd + 1);
|
||||
|
||||
for (i = 1; i < pd_port->uvdm_cnt; i++)
|
||||
reply_cmd[i] = ~pd_port->uvdm_data[i];
|
||||
|
||||
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, pd_port->uvdm_cnt, reply_cmd);
|
||||
VDM_STATE_NORESP_CMD(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_RICHTEK_UVDM */
|
1367
drivers/usb/typec/platform/external/tcpc/pd_policy_engine.c
vendored
Normal file
1367
drivers/usb/typec/platform/external/tcpc/pd_policy_engine.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
369
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_com.c
vendored
Normal file
369
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_com.c
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for Common
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
static inline void *pd_get_tcp_event_data_object(struct pd_port *pd_port)
|
||||
{
|
||||
return pd_port->tcp_event.tcp_dpm_data.data_object;
|
||||
}
|
||||
|
||||
/*
|
||||
* Policy Engine General State Activity
|
||||
*/
|
||||
|
||||
static void pe_idle_reset_data(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reset_pe_timer(pd_port);
|
||||
pd_reset_svid_data(pd_port);
|
||||
|
||||
pd_port->pe_data.pd_prev_connected = false;
|
||||
pd_port->state_machine = PE_STATE_MACHINE_IDLE;
|
||||
|
||||
pd_enable_bist_test_mode(pd_port, false);
|
||||
|
||||
#ifndef CONFIG_USB_PD_TRANSMIT_BIST2
|
||||
pd_disable_bist_mode2(pd_port);
|
||||
#endif /* CONFIG_USB_PD_TRANSMIT_BIST2 */
|
||||
|
||||
pd_enable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT);
|
||||
|
||||
pd_unlock_msg_output(pd_port);
|
||||
}
|
||||
|
||||
void pe_idle1_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
||||
pd_port->error_recovery_once = 0;
|
||||
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
||||
|
||||
pe_idle_reset_data(pd_port);
|
||||
pd_try_put_pe_idle_event(pd_port);
|
||||
}
|
||||
|
||||
void pe_idle2_entry(struct pd_port *pd_port)
|
||||
{
|
||||
memset(&pd_port->pe_data, 0, sizeof(struct pe_data));
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
|
||||
pd_disable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT);
|
||||
pd_notify_pe_idle(pd_port);
|
||||
}
|
||||
|
||||
void pe_reject_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_REJECT);
|
||||
}
|
||||
|
||||
void pe_error_recovery_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
||||
pd_port->error_recovery_once++;
|
||||
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
||||
|
||||
pe_idle_reset_data(pd_port);
|
||||
pd_notify_pe_error_recovery(pd_port);
|
||||
pd_try_put_pe_idle_event(pd_port);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
||||
void pe_error_recovery_once_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t state = PD_CONNECT_TYPEC_ONLY_SNK_DFT;
|
||||
|
||||
pd_notify_pe_hard_reset_completed(pd_port);
|
||||
|
||||
if (pd_port->power_role == PD_ROLE_SOURCE) {
|
||||
state = PD_CONNECT_TYPEC_ONLY_SRC;
|
||||
pd_dpm_dynamic_disable_vconn(pd_port);
|
||||
} else
|
||||
pd_dpm_sink_vbus(pd_port, true);
|
||||
|
||||
pd_update_connect_state(pd_port, state);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
|
||||
void pe_over_recv_hreset_limit_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_INFO("OverHResetLimit++\n");
|
||||
pe_idle_reset_data(pd_port);
|
||||
pd_notify_pe_over_recv_hreset(pd_port);
|
||||
PE_INFO("OverHResetLimit--\n");
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */
|
||||
|
||||
void pe_bist_test_data_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_IGNORE_UNKNOWN_EVENT(pd_port);
|
||||
|
||||
pd_enable_bist_test_mode(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_bist_test_data_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_bist_test_mode(pd_port, false);
|
||||
}
|
||||
|
||||
void pe_bist_carrier_mode_2_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_bist_mode2(pd_port);
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_BIST_CONT_MODE);
|
||||
}
|
||||
|
||||
void pe_bist_carrier_mode_2_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_disable_bist_mode2(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Policy Engine Share State Activity
|
||||
*/
|
||||
|
||||
static inline uint8_t pe30_power_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP;
|
||||
|
||||
if (pd_port->vconn_role)
|
||||
rx_cap = PD_RX_CAP_PE_READY_DFP;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
dpm_reaction_clear(pd_port, DPM_REACTION_DFP_FLOW_DELAY |
|
||||
DPM_REACTION_UFP_FLOW_DELAY);
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
|
||||
return rx_cap;
|
||||
}
|
||||
|
||||
static inline uint8_t pe20_power_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP;
|
||||
|
||||
if (pd_port->data_role == PD_ROLE_DFP)
|
||||
rx_cap = PD_RX_CAP_PE_READY_DFP;
|
||||
|
||||
return rx_cap;
|
||||
}
|
||||
|
||||
void pe_power_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t rx_cap;
|
||||
|
||||
pd_port->state_machine = PE_STATE_MACHINE_NORMAL;
|
||||
|
||||
pd_port->pe_data.during_swap = false;
|
||||
pd_port->pe_data.explicit_contract = true;
|
||||
|
||||
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
|
||||
pd_port->pe_data.renegotiation_count = 0;
|
||||
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
|
||||
|
||||
if (pd_check_rev30(pd_port))
|
||||
rx_cap = pe30_power_ready_entry(pd_port);
|
||||
else
|
||||
rx_cap = pe20_power_ready_entry(pd_port);
|
||||
|
||||
pd_set_rx_enable(pd_port, rx_cap);
|
||||
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_CAP_READY_ONCE);
|
||||
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-85 Get Battery Capabilities State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
|
||||
void pe_get_battery_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
struct pd_get_battery_capabilities *gbcdb =
|
||||
pd_get_tcp_event_data_object(pd_port);
|
||||
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ext_msg(pd_port, PD_EXT_GET_BAT_CAP, PD_GBCDB_SIZE, gbcdb);
|
||||
}
|
||||
|
||||
void pe_get_battery_cap_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_battery_cap(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-86 Give Battery Capabilities State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
|
||||
void pe_give_battery_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_battery_cap(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-87 Get Battery Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
|
||||
void pe_get_battery_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
struct pd_get_battery_status *gbsdb =
|
||||
pd_get_tcp_event_data_object(pd_port);
|
||||
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ext_msg(pd_port, PD_EXT_GET_BAT_STATUS, PD_GBSDB_SIZE,
|
||||
gbsdb);
|
||||
}
|
||||
|
||||
void pe_get_battery_status_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_battery_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-88 Give Battery Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
|
||||
void pe_give_battery_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_battery_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-89 Get Manufacturer Information State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
|
||||
|
||||
void pe_get_manufacturer_info_entry(struct pd_port *pd_port)
|
||||
{
|
||||
struct pd_get_manufacturer_info *gmidb =
|
||||
pd_get_tcp_event_data_object(pd_port);
|
||||
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ext_msg(pd_port, PD_EXT_GET_MFR_INFO, PD_GMIDB_SIZE, gmidb);
|
||||
}
|
||||
|
||||
void pe_get_manufacturer_info_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_mfrs_info(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-90 Give Manufacturer Information State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
|
||||
|
||||
void pe_give_manufacturer_info_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_mfrs_info(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-91 Get Country Codes State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
|
||||
void pe_get_country_codes_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_COUNTRY_CODE);
|
||||
}
|
||||
|
||||
void pe_get_country_codes_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_country_codes(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-92 Give Country Codes State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
|
||||
void pe_give_country_codes_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_country_codes(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-93 Get Country Information State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
|
||||
void pe_get_country_info_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t *ccdo = pd_get_tcp_event_data_object(pd_port);
|
||||
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_data_msg(pd_port, PD_DATA_GET_COUNTRY_INFO, PD_CCDO_SIZE,
|
||||
ccdo);
|
||||
}
|
||||
|
||||
void pe_get_country_info_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_country_info(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-94 Give Country Information State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
|
||||
void pe_give_country_info_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_country_info(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Unsupported, Unrecognized UVDM and Unsupported SVDM.
|
||||
*/
|
||||
|
||||
void pe_vdm_not_supported_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
46
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dbg.c
vendored
Normal file
46
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dbg.c
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for DBGACC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
|
||||
void pe_dbg_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t state;
|
||||
|
||||
if (pd_port->pe_data.pe_ready)
|
||||
return;
|
||||
|
||||
pd_port->pe_data.pe_ready = true;
|
||||
pd_reset_protocol_layer(pd_port, false);
|
||||
|
||||
if (pd_port->data_role == PD_ROLE_UFP) {
|
||||
PE_INFO("Custom_DBGACC : UFP\n");
|
||||
state = PD_CONNECT_PE_READY_DBGACC_UFP;
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_UFP);
|
||||
} else {
|
||||
PE_INFO("Custom_DBGACC : DFP\n");
|
||||
state = PD_CONNECT_PE_READY_DBGACC_DFP;
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_DFP);
|
||||
}
|
||||
|
||||
pd_update_connect_state(pd_port, state);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
221
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dfp.c
vendored
Normal file
221
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dfp.c
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for DFP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-64 DFP to UFP VDM Discover Identity State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_ufp_vdm_identity_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP);
|
||||
}
|
||||
|
||||
void pe_dfp_ufp_vdm_identity_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_id(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_ufp_vdm_identity_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_id(pd_port, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-65 DFP VDM Discover Identity State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_cbl_vdm_identity_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_port->pe_data.discover_id_counter++;
|
||||
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
|
||||
}
|
||||
|
||||
void pe_dfp_cbl_vdm_identity_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_cable_id(pd_port, false);
|
||||
}
|
||||
|
||||
void pe_dfp_cbl_vdm_identity_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_cable_id(pd_port, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-66 DFP VDM Discover SVIDs State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_vdm_svids_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_vdm_discover_svids(pd_port, TCPC_TX_SOP);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_svids_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_svids(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_svids_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_svids(pd_port, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-67 DFP VDM Discover Modes State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_vdm_modes_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_vdm_discover_modes(pd_port, TCPC_TX_SOP, pd_port->mode_svid);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_modes_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_modes(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_modes_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_modes(pd_port, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-68 DFP VDM Mode Entry State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_vdm_mode_entry_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_vdm_enter_mode(pd_port, TCPC_TX_SOP, pd_port->mode_svid,
|
||||
pd_port->mode_obj_pos);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_mode_entry_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_enter_mode(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_mode_entry_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_enter_mode(pd_port, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-69 DFP VDM Mode Exit State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_vdm_mode_exit_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_vdm_exit_mode(pd_port, TCPC_TX_SOP, pd_port->mode_svid,
|
||||
pd_port->mode_obj_pos);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_mode_exit_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_exit_mode(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-70 DFP VDM Attention State Diagram
|
||||
*/
|
||||
|
||||
void pe_dfp_vdm_attention_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_attention(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-83 DFP Cable Soft Reset or Cable Reset State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
|
||||
void pe_dfp_cbl_send_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG_OR_TX_FAILED(pd_port);
|
||||
|
||||
pd_send_cable_soft_reset(pd_port);
|
||||
}
|
||||
|
||||
void pe_dfp_cbl_send_cable_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
/* TODO : we don't do cable reset now */
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
|
||||
/*
|
||||
* [PD2.0] Display Port
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
||||
|
||||
void pe_dfp_vdm_dp_status_update_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_send_dp_status_update(pd_port);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_dp_status_update_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_dp_status_update(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_dp_status_update_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_dp_status_update(pd_port, false);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_dp_configuration_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_send_dp_configuration(pd_port);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_dp_configuration_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_dp_configuration(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_vdm_dp_configuration_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_dp_configuration(pd_port, false);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
|
||||
|
||||
/*
|
||||
* UVDM
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_VDM
|
||||
|
||||
void pe_dfp_uvdm_send_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_send_uvdm(pd_port);
|
||||
}
|
||||
|
||||
void pe_dfp_uvdm_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_uvdm(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_dfp_uvdm_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dfp_inform_uvdm(pd_port, false);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
|
87
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dr.c
vendored
Normal file
87
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_dr.c
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for DR
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0]
|
||||
* Figure 8-53 Dual-Role (Source) Get Source Capabilities diagram
|
||||
* Figure 8-54 Dual-Role (Source) Give Sink Capabilities diagram
|
||||
* Figure 8-55 Dual-Role (Sink) Get Sink Capabilities State Diagram
|
||||
* Figure 8-56 Dual-Role (Sink) Give Source Capabilities State Diagram
|
||||
*/
|
||||
|
||||
void pe_dr_src_get_source_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG_OR_RJ(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP);
|
||||
}
|
||||
|
||||
void pe_dr_src_get_source_cap_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dr_inform_source_cap(pd_port);
|
||||
}
|
||||
|
||||
void pe_dr_src_give_sink_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_sink_caps(pd_port);
|
||||
}
|
||||
|
||||
void pe_dr_snk_get_sink_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG_OR_RJ(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP);
|
||||
}
|
||||
|
||||
void pe_dr_snk_get_sink_cap_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dr_inform_sink_cap(pd_port);
|
||||
}
|
||||
|
||||
void pe_dr_snk_give_source_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_source_caps(pd_port);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
void pe_dr_snk_give_source_cap_ext_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_source_cap_ext(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
void pe_dr_src_get_source_cap_ext_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT);
|
||||
}
|
||||
|
||||
void pe_dr_src_get_source_cap_ext_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_source_cap_ext(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
81
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_drs.c
vendored
Normal file
81
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_drs.c
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for DRS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-49: Type-C DFP to UFP Data Role Swap State Diagram
|
||||
*/
|
||||
|
||||
void pe_drs_dfp_ufp_evaluate_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_UFP);
|
||||
}
|
||||
|
||||
void pe_drs_dfp_ufp_accept_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
|
||||
}
|
||||
|
||||
void pe_drs_dfp_ufp_change_to_ufp_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_drs_change_role(pd_port, PD_ROLE_UFP);
|
||||
}
|
||||
|
||||
void pe_drs_dfp_ufp_send_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP);
|
||||
}
|
||||
|
||||
void pe_drs_dfp_ufp_reject_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-50: Type-C UFP to DFP Data Role Swap State Diagram
|
||||
*/
|
||||
|
||||
void pe_drs_ufp_dfp_evaluate_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_DFP);
|
||||
}
|
||||
|
||||
void pe_drs_ufp_dfp_accept_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
|
||||
}
|
||||
|
||||
void pe_drs_ufp_dfp_change_to_dfp_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_RESET_CABLE
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
|
||||
#endif /* CONFIG_USB_PD_RESET_CABLE */
|
||||
|
||||
pd_dpm_drs_change_role(pd_port, PD_ROLE_DFP);
|
||||
}
|
||||
|
||||
void pe_drs_ufp_dfp_send_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP);
|
||||
}
|
||||
|
||||
void pe_drs_ufp_dfp_reject_dr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg(pd_port);
|
||||
}
|
121
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_prs.c
vendored
Normal file
121
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_prs.c
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for PRS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-51:
|
||||
* Dual-Role Port in Source to Sink Power Role Swap State Diagram
|
||||
*/
|
||||
|
||||
void pe_prs_src_snk_evaluate_pr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SINK);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_accept_pr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_notify_pe_execute_pr_swap(pd_port, true);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_transition_to_off_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_lock_msg_output(pd_port); /* for tSRCTransition */
|
||||
pd_notify_pe_execute_pr_swap(pd_port, true);
|
||||
|
||||
pd_enable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_assert_rd_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_prs_change_role(pd_port, PD_ROLE_SINK);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_wait_source_on_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_send_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP);
|
||||
}
|
||||
|
||||
void pe_prs_src_snk_reject_pr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-52:
|
||||
* Dual-role Port in Sink to Source Power Role Swap State Diagram
|
||||
*/
|
||||
|
||||
void pe_prs_snk_src_evaluate_pr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SOURCE);
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_accept_pr_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_notify_pe_execute_pr_swap(pd_port, true);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_transition_to_off_entry(struct pd_port *pd_port)
|
||||
{
|
||||
/*
|
||||
* Sink should call pd_notify_pe_execute_pr_swap before this state,
|
||||
* because source may turn off power & change CC before we got
|
||||
* GoodCRC or Accept.
|
||||
*/
|
||||
|
||||
pd_port->pe_data.during_swap = true;
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_OFF);
|
||||
pd_dpm_prs_turn_off_power_sink(pd_port);
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_assert_rp_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_prs_change_role(pd_port, PD_ROLE_SOURCE);
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_source_on_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_RESET_CABLE
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
|
||||
#endif /* CONFIG_USB_PD_RESET_CABLE */
|
||||
|
||||
pd_dpm_dynamic_enable_vconn(pd_port);
|
||||
pd_dpm_prs_enable_power_source(pd_port, true);
|
||||
|
||||
/* Send PS_Rdy in process_event after source_on */
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_send_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_notify_pe_execute_pr_swap(pd_port, false);
|
||||
pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP);
|
||||
}
|
||||
|
||||
void pe_prs_snk_src_reject_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg(pd_port);
|
||||
}
|
324
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_snk.c
vendored
Normal file
324
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_snk.c
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for SNK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-39 Sink Port state diagram
|
||||
*/
|
||||
|
||||
void pe_snk_startup_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t rx_cap = PD_RX_CAP_PE_STARTUP;
|
||||
bool pr_swap = pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP;
|
||||
|
||||
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
|
||||
uint8_t msg_id_last = pd_port->pe_data.msg_id_rx[TCPC_TX_SOP];
|
||||
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
|
||||
|
||||
pd_reset_protocol_layer(pd_port, false);
|
||||
|
||||
if (pr_swap) {
|
||||
/*
|
||||
* If PE reset rx_cap to startup in here,
|
||||
* maybe can't meet tSwapSink for PR_SWAP case
|
||||
*/
|
||||
rx_cap = PD_RX_CAP_PE_SEND_WAIT_CAP;
|
||||
|
||||
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
|
||||
pd_port->msg_id_pr_swap_last = msg_id_last;
|
||||
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
|
||||
}
|
||||
|
||||
pd_set_rx_enable(pd_port, rx_cap);
|
||||
pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
|
||||
}
|
||||
|
||||
void pe_snk_discovery_entry(struct pd_port *pd_port)
|
||||
{
|
||||
bool wait_valid = true;
|
||||
|
||||
if (pd_check_pe_during_hard_reset(pd_port)) {
|
||||
wait_valid = false;
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION);
|
||||
}
|
||||
pd_enable_vbus_valid_detection(pd_port, wait_valid);
|
||||
}
|
||||
|
||||
void pe_snk_wait_for_capabilities_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW
|
||||
/* Default current draw after HardReset */
|
||||
if (pd_check_pe_during_hard_reset(pd_port))
|
||||
pd_dpm_sink_vbus(pd_port, true);
|
||||
#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */
|
||||
|
||||
pd_notify_pe_hard_reset_completed(pd_port);
|
||||
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
|
||||
}
|
||||
|
||||
void pe_snk_evaluate_capability_entry(struct pd_port *pd_port)
|
||||
{
|
||||
/* Disable UART output for Source SenderResponse */
|
||||
pd_lock_msg_output(pd_port);
|
||||
|
||||
pd_handle_hard_reset_recovery(pd_port);
|
||||
pd_handle_first_pd_command(pd_port);
|
||||
|
||||
pd_port->pe_data.explicit_contract = false;
|
||||
pd_dpm_snk_evaluate_caps(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_select_capability_entry(struct pd_port *pd_port)
|
||||
{
|
||||
struct pd_event *pd_event = pd_get_curr_pd_event(pd_port);
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port);
|
||||
|
||||
if (pd_event->event_type == PD_EVT_DPM_MSG) {
|
||||
PE_DBG("SelectCap%d, rdo:0x%08x\n", pd_event->msg_sec,
|
||||
pd_port->last_rdo);
|
||||
} else {
|
||||
/* new request, for debug only */
|
||||
/* pd_dpm_sink_vbus(pd_port, false); */
|
||||
PE_DBG("NewReq, rdo:0x%08x\n", pd_port->last_rdo);
|
||||
}
|
||||
|
||||
/* Disable UART output for Sink SenderResponse */
|
||||
pd_lock_msg_output(pd_port);
|
||||
|
||||
pd_send_sop_data_msg(pd_port, PD_DATA_REQUEST, 1, &pd_port->last_rdo);
|
||||
}
|
||||
|
||||
void pe_snk_select_capability_exit(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
|
||||
|
||||
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_ACCEPT)) {
|
||||
pd_port->pe_data.remote_selected_cap =
|
||||
RDO_POS(pd_port->last_rdo);
|
||||
pd_port->cap_miss_match = 0;
|
||||
} else if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_REJECT)) {
|
||||
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
|
||||
if (pd_port->cap_miss_match == 0x01) {
|
||||
PE_INFO("reset renegotiation cnt by cap mismatch\n");
|
||||
pd_port->pe_data.renegotiation_count = 0;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
|
||||
pd_port->cap_miss_match |= (1 << 1);
|
||||
} else
|
||||
pd_port->cap_miss_match = 0;
|
||||
|
||||
/* Waiting for Hard-Reset Done */
|
||||
if (!pd_check_timer_msg_event(pd_port, PD_TIMER_SENDER_RESPONSE))
|
||||
pd_unlock_msg_output(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_transition_sink_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION);
|
||||
|
||||
#ifdef CONFIG_USB_PD_SNK_GOTOMIN
|
||||
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_GOTO_MIN)) {
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK)
|
||||
pd_port->request_i_new = pd_port->request_i_op;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_SNK_GOTOMIN */
|
||||
|
||||
pd_dpm_snk_standby_power(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_WAIT))
|
||||
pd_enable_timer(pd_port, PD_TIMER_SINK_REQUEST);
|
||||
|
||||
pd_notify_pe_snk_explicit_contract(pd_port);
|
||||
pe_power_ready_entry(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_hard_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
int rv = 0;
|
||||
uint32_t chip_id = 0;
|
||||
|
||||
pd_send_hard_reset(pd_port);
|
||||
rv = tcpci_get_chip_id(pd_port->tcpc, &chip_id);
|
||||
if (!rv && SC2150A_DID == chip_id) {
|
||||
pd_enable_timer(pd_port, PD_TIMER_HARD_RESET_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_snk_transition_to_default_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reset_local_hw(pd_port);
|
||||
pd_dpm_snk_hard_reset(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_give_sink_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_sink_caps(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_get_source_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_TCPM_CB_2ND
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
#else
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
#endif /* CONFIG_USB_PD_TCPM_CB_2ND */
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP);
|
||||
}
|
||||
|
||||
void pe_snk_send_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_soft_reset(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_handle_soft_reset(pd_port);
|
||||
}
|
||||
|
||||
/* ---- Policy Engine (PD30) ---- */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-71 Sink Port Not Supported Message State Diagram
|
||||
*/
|
||||
|
||||
void pe_snk_send_not_supported_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void pe_snk_not_supported_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_DPM_INFORMED(pd_port);
|
||||
|
||||
pd_dpm_inform_not_support(pd_port);
|
||||
}
|
||||
|
||||
void pe_snk_chunk_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-74 Sink Port Source Alert State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
void pe_snk_source_alert_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_DPM_INFORMED(pd_port);
|
||||
|
||||
pd_dpm_inform_alert(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-75 Sink Port Sink Alert State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
void pe_snk_send_sink_alert_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
pd_dpm_send_alert(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-77 Sink Port Get Source Capabilities Extended State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
void pe_snk_get_source_cap_ext_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT);
|
||||
}
|
||||
|
||||
void pe_snk_get_source_cap_ext_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_source_cap_ext(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-79 Sink Port Get Source Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
||||
void pe_snk_get_source_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS);
|
||||
}
|
||||
|
||||
void pe_snk_get_source_status_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_status(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-82 Sink Give Sink Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
void pe_snk_give_sink_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-83 Sink Port Get Source PPS Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
void pe_snk_get_pps_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_PPS_STATUS);
|
||||
}
|
||||
|
||||
void pe_snk_get_pps_status_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_pps_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
309
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_src.c
vendored
Normal file
309
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_src.c
vendored
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for SRC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-38 Source Port Policy Engine state diagram
|
||||
*/
|
||||
|
||||
void pe_src_startup_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reset_protocol_layer(pd_port, false);
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_STARTUP);
|
||||
|
||||
/*
|
||||
* When entering this state,
|
||||
* VBUS must be valid even for cc_attached.
|
||||
*/
|
||||
|
||||
pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
|
||||
}
|
||||
|
||||
void pe_src_discovery_entry(struct pd_port *pd_port)
|
||||
{
|
||||
/* The SourceCapabilitiesTimer continues to run during the states
|
||||
* defined in Source Startup Structured VDM Discover Identity State
|
||||
* Diagram
|
||||
*/
|
||||
|
||||
pd_port->pe_data.pd_connected = false;
|
||||
|
||||
pd_enable_timer(pd_port, PD_TIMER_SOURCE_CAPABILITY);
|
||||
|
||||
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
|
||||
if (pd_is_discover_cable(pd_port))
|
||||
pd_enable_timer(pd_port, PD_TIMER_DISCOVER_ID);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pe_src_send_capabilities_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port);
|
||||
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
|
||||
|
||||
pd_dpm_send_source_caps(pd_port);
|
||||
pd_port->pe_data.cap_counter++;
|
||||
}
|
||||
|
||||
void pe_src_negotiate_capabilities_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_handle_first_pd_command(pd_port);
|
||||
pd_dpm_src_evaluate_request(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_transition_supply_entry(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t msg = PD_CTRL_ACCEPT;
|
||||
struct pd_event *pd_event = pd_get_curr_pd_event(pd_port);
|
||||
|
||||
/* goto-min */
|
||||
if (pd_event->event_type == PD_EVT_TCP_MSG) {
|
||||
msg = PD_CTRL_GOTO_MIN;
|
||||
pd_port->request_i_new = pd_port->request_i_op;
|
||||
}
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, msg);
|
||||
}
|
||||
|
||||
void pe_src_transition_supply2_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
|
||||
}
|
||||
|
||||
void pe_src_ready_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_notify_pe_src_explicit_contract(pd_port);
|
||||
pe_power_ready_entry(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_disabled_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_notify_pe_hard_reset_completed(pd_port);
|
||||
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISABLE);
|
||||
pd_update_connect_state(pd_port, PD_CONNECT_TYPEC_ONLY_SRC);
|
||||
pd_dpm_dynamic_disable_vconn(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_capability_response_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg_no_resp(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_hard_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_hard_reset(pd_port);
|
||||
pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
|
||||
pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
|
||||
}
|
||||
|
||||
void pe_src_hard_reset_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
|
||||
pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
|
||||
}
|
||||
|
||||
void pe_src_transition_to_default_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reset_local_hw(pd_port);
|
||||
pd_dpm_src_hard_reset(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_transition_to_default_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_set_vconn(pd_port, PD_ROLE_VCONN_ON);
|
||||
}
|
||||
|
||||
void pe_src_get_sink_cap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP);
|
||||
}
|
||||
|
||||
void pe_src_get_sink_cap_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_dr_inform_sink_cap(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_wait_new_capabilities_entry(struct pd_port *pd_port)
|
||||
{
|
||||
/* Wait for new Source Capabilities */
|
||||
}
|
||||
|
||||
void pe_src_send_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_soft_reset(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_handle_soft_reset(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-81
|
||||
Source Startup Structured VDM Discover Identity State Diagram (TODO)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
|
||||
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
void pe_src_cbl_send_soft_reset_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_RESPONSE(pd_port);
|
||||
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE);
|
||||
|
||||
pd_send_cable_soft_reset(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
|
||||
void pe_src_vdm_identity_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE);
|
||||
|
||||
pd_port->pe_data.discover_id_counter++;
|
||||
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
|
||||
}
|
||||
|
||||
void pe_src_vdm_identity_acked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_cable_id(pd_port, true);
|
||||
}
|
||||
|
||||
void pe_src_vdm_identity_naked_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_cable_id(pd_port, true);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
/*
|
||||
* [PD3.0] Source Port Not Supported Message State Diagram
|
||||
*/
|
||||
|
||||
void pe_src_send_not_supported_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void pe_src_not_supported_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_DPM_INFORMED(pd_port);
|
||||
|
||||
pd_dpm_inform_not_support(pd_port);
|
||||
}
|
||||
|
||||
void pe_src_chunk_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-73 Source Port Source Alert State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
void pe_src_send_source_alert_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
pd_dpm_send_alert(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-76 Source Port Sink Alert State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
void pe_src_sink_alert_received_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_DPM_INFORMED(pd_port);
|
||||
|
||||
pd_dpm_inform_alert(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-78 Source Give Source Capabilities Extended State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
void pe_src_give_source_cap_ext_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_source_cap_ext(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-80 Source Give Source Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
void pe_src_give_source_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
pd_dpm_send_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-81 Source Port Get Sink Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
||||
void pe_src_get_sink_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_MSG(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS);
|
||||
}
|
||||
|
||||
void pe_src_get_sink_status_exit(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_inform_status(pd_port);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
||||
|
||||
/*
|
||||
* [PD3.0] Figure 8-84 Source Give Source PPS Status State Diagram
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
|
||||
void pe_src_give_pps_status_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
|
||||
/* TODO */
|
||||
PD_BUG_ON(1);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
122
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_ufp.c
vendored
Normal file
122
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_ufp.c
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for UFP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-58 UFP Structured VDM Discover Identity State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_get_identity_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_id_info(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-59 UFP Structured VDM Discover SVIDs State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_get_svids_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_svid_info(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-60 UFP Structured VDM Discover Modes State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_get_modes_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_mode_info(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-61 UFP Structured VDM Enter Mode State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_evaluate_mode_entry_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_enter_mode(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-62 UFP Structured VDM Exit Mode State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_mode_exit_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_exit_mode(pd_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-63 UFP VDM Attention State Diagram
|
||||
*/
|
||||
|
||||
void pe_ufp_vdm_attention_request_entry(struct pd_port *pd_port)
|
||||
{
|
||||
VDM_STATE_NORESP_CMD(pd_port);
|
||||
|
||||
switch (pd_port->mode_svid) {
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE
|
||||
case USB_SID_DISPLAYPORT:
|
||||
pd_dpm_ufp_send_dp_attention(pd_port);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pd_send_vdm_attention(pd_port, TCPC_TX_SOP, pd_port->mode_svid,
|
||||
pd_port->mode_obj_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ALT Mode
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE
|
||||
|
||||
void pe_ufp_vdm_dp_status_update_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_dp_status(pd_port);
|
||||
}
|
||||
|
||||
void pe_ufp_vdm_dp_configure_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_request_dp_config(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE */
|
||||
|
||||
/*
|
||||
* SVMD/UVDM
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_VDM
|
||||
|
||||
void pe_ufp_uvdm_recv_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_recv_uvdm(pd_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
|
||||
|
||||
void pe_ufp_vdm_send_nak_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_ufp_send_svdm_nak(pd_port);
|
||||
VDM_STATE_DPM_INFORMED(pd_port);
|
||||
}
|
71
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_vcs.c
vendored
Normal file
71
drivers/usb/typec/platform/external/tcpc/pd_policy_engine_vcs.c
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for VCS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci.h>
|
||||
#include <linux/usb/tcpc/pd_policy_engine.h>
|
||||
|
||||
/*
|
||||
* [PD2.0] Figure 8-57 VCONN Swap State Diagram
|
||||
*/
|
||||
|
||||
void pe_vcs_send_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pe_send_swap_request_entry(pd_port, PD_CTRL_VCONN_SWAP);
|
||||
}
|
||||
|
||||
void pe_vcs_evaluate_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_dpm_vcs_evaluate_swap(pd_port);
|
||||
}
|
||||
|
||||
void pe_vcs_accept_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
|
||||
}
|
||||
|
||||
void pe_vcs_reject_vconn_swap_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_reply_wait_reject_msg(pd_port);
|
||||
}
|
||||
|
||||
void pe_vcs_wait_for_vconn_entry(struct pd_port *pd_port)
|
||||
{
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_VCONN_ON);
|
||||
}
|
||||
|
||||
void pe_vcs_turn_off_vconn_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_DPM_ACK(pd_port);
|
||||
pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_OFF);
|
||||
}
|
||||
|
||||
void pe_vcs_turn_on_vconn_entry(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_RESET_CABLE
|
||||
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
|
||||
#endif /* CONFIG_USB_PD_RESET_CABLE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_DYNAMIC_ON);
|
||||
}
|
||||
|
||||
void pe_vcs_send_ps_rdy_entry(struct pd_port *pd_port)
|
||||
{
|
||||
PE_STATE_WAIT_TX_SUCCESS(pd_port);
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
|
||||
}
|
835
drivers/usb/typec/platform/external/tcpc/pd_process_evt.c
vendored
Normal file
835
drivers/usb/typec/platform/external/tcpc/pd_process_evt.c
vendored
Normal file
@ -0,0 +1,835 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
|
||||
/*
|
||||
* [BLOCK] print event
|
||||
*/
|
||||
|
||||
#if PE_EVENT_DBG_ENABLE
|
||||
static const char *const pd_ctrl_msg_name[] = {
|
||||
"ctrl0", "good_crc", "goto_min", "accept",
|
||||
"reject", "ping", "ps_rdy", "get_src_cap",
|
||||
"get_snk_cap", "dr_swap", "pr_swap", "vs_swap",
|
||||
"wait", "soft_reset", "ctrlE", "ctrlF",
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
"no_support", "get_src_cap_ex", "get_status", "fr_swap",
|
||||
"get_pps", "get_cc",
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
};
|
||||
|
||||
static inline void print_ctrl_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_CTRL_MSG_NR)
|
||||
PE_EVT_INFO("%s\n", pd_ctrl_msg_name[msg]);
|
||||
}
|
||||
|
||||
static const char *const pd_data_msg_name[] = {
|
||||
"data0", "src_cap", "request", "bist", "sink_cap",
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
"bat_status", "alert", "get_ci",
|
||||
#else
|
||||
"data5", "data6", "data7",
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
"data8", "data9", "dataA", "dataB", "dataC",
|
||||
"dataD", "dataE", "vdm",
|
||||
};
|
||||
|
||||
static inline void print_data_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_DATA_MSG_NR)
|
||||
PE_EVT_INFO("%s\n", pd_data_msg_name[msg]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
static const char *const pd_ext_msg_name[] = {
|
||||
"ext0",
|
||||
"src_cap_ex",
|
||||
"status",
|
||||
"get_bat_cap",
|
||||
"get_bat_status",
|
||||
"bat_cap",
|
||||
"get_mfr_info",
|
||||
"mfr_info",
|
||||
"sec_req",
|
||||
"sec_resp",
|
||||
"fw_update_req",
|
||||
"fw_update_res",
|
||||
"pps_status",
|
||||
"ci",
|
||||
"cc",
|
||||
};
|
||||
|
||||
static inline void print_ext_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_EXT_MSG_NR)
|
||||
PE_EVT_INFO("%s\n", pd_ext_msg_name[msg]);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
static const char *const pd_hw_msg_name[] = {
|
||||
"Detached", "Attached", "hard_reset", "vbus_high", "vbus_low",
|
||||
"vbus_0v", "vbus_stable", "tx_err", "discard", "retry_vdm",
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
"sink_tx_change",
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
};
|
||||
|
||||
static inline void print_hw_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_HW_MSG_NR)
|
||||
PE_EVT_INFO("%s\n", pd_hw_msg_name[msg]);
|
||||
}
|
||||
|
||||
static const char *const pd_pe_msg_name[] = {
|
||||
"reset_prl_done", "pr_at_dft", "hard_reset_done",
|
||||
"pe_idle", "vdm_reset", "vdm_not_support",
|
||||
};
|
||||
|
||||
static inline void print_pe_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_PE_MSG_NR)
|
||||
PE_EVT_INFO("%s\n", pd_pe_msg_name[msg]);
|
||||
}
|
||||
|
||||
static const char *const pd_dpm_msg_name[] = {
|
||||
"ack",
|
||||
"nak",
|
||||
"cap_change",
|
||||
"not_support",
|
||||
};
|
||||
|
||||
static inline void print_dpm_msg_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < PD_DPM_MSG_NR)
|
||||
PE_EVT_INFO("dpm_%s\n", pd_dpm_msg_name[msg]);
|
||||
}
|
||||
|
||||
static const char *const tcp_dpm_evt_name[] = {
|
||||
/* TCP_DPM_EVT_UNKONW */
|
||||
"unknown",
|
||||
|
||||
/* TCP_DPM_EVT_PD_COMMAND */
|
||||
"pr_swap_snk",
|
||||
"pr_swap_src",
|
||||
"dr_swap_ufp",
|
||||
"dr_swap_dfp",
|
||||
"vc_swap_off",
|
||||
"vc_swap_on",
|
||||
"goto_min",
|
||||
"soft_reset",
|
||||
"cable_soft_reset",
|
||||
"get_src_cap",
|
||||
"get_snk_cap",
|
||||
"request",
|
||||
"request_ex",
|
||||
"request_again",
|
||||
"bist_cm2",
|
||||
"dummy",
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
"get_src_cap_ext",
|
||||
"get_status",
|
||||
"fr_swap_snk",
|
||||
"fr_swap_src",
|
||||
"get_cc",
|
||||
"get_pps",
|
||||
|
||||
"alert",
|
||||
"get_ci",
|
||||
"get_bat_cap",
|
||||
"get_bat_status",
|
||||
"get_mfrs_info",
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
/* TCP_DPM_EVT_VDM_COMMAND */
|
||||
"disc_cable",
|
||||
"disc_id",
|
||||
"disc_svid",
|
||||
"disc_mode",
|
||||
"enter_mode",
|
||||
"exit_mode",
|
||||
"attention",
|
||||
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE
|
||||
"dp_atten",
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
||||
"dp_status",
|
||||
"dp_config",
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
|
||||
#endif /* CONFIG_USB_PD_ALT_MODE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_VDM
|
||||
"uvdm",
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
|
||||
|
||||
/* TCP_DPM_EVT_IMMEDIATELY */
|
||||
"hard_reset",
|
||||
"error_recovery",
|
||||
};
|
||||
|
||||
static inline void print_tcp_event(struct tcpc_device *tcpc, uint8_t msg)
|
||||
{
|
||||
if (msg < TCP_DPM_EVT_NR)
|
||||
PE_EVT_INFO("tcp_event(%s), %d\n", tcp_dpm_evt_name[msg], msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void print_event(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#if PE_EVENT_DBG_ENABLE
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
print_ctrl_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
|
||||
case PD_EVT_DATA_MSG:
|
||||
print_data_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_EVT_EXT_MSG:
|
||||
print_ext_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
print_dpm_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
|
||||
case PD_EVT_HW_MSG:
|
||||
print_hw_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
|
||||
case PD_EVT_PE_MSG:
|
||||
print_pe_msg_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
PE_EVT_INFO("timer\n");
|
||||
break;
|
||||
|
||||
case PD_EVT_TCP_MSG:
|
||||
print_tcp_event(tcpc, pd_event->msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
bool pd_make_pe_state_transit(struct pd_port *pd_port, uint8_t curr_state,
|
||||
const struct pe_state_reaction *state_reaction)
|
||||
{
|
||||
int i;
|
||||
const struct pe_state_transition *state_transition =
|
||||
state_reaction->state_transition;
|
||||
|
||||
for (i = 0; i < state_reaction->nr_transition; i++) {
|
||||
if (state_transition[i].curr_state == curr_state) {
|
||||
PE_TRANSIT_STATE(pd_port,
|
||||
state_transition[i].next_state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static inline bool pd_process_ready_protocol_error(struct pd_port *pd_port)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
bool multi_chunk;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
if (!pd_port->curr_unsupported_msg) {
|
||||
pe_transit_soft_reset_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pd_check_rev30(pd_port)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_REJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
multi_chunk = pd_is_multi_chunk_msg(pd_port);
|
||||
|
||||
if (pd_port->power_role == PD_ROLE_SINK) {
|
||||
PE_TRANSIT_STATE(pd_port, multi_chunk ?
|
||||
PE_SNK_CHUNK_RECEIVED :
|
||||
PE_SNK_SEND_NOT_SUPPORTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, multi_chunk ? PE_SRC_CHUNK_RECEIVED :
|
||||
PE_SRC_SEND_NOT_SUPPORTED);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
bool pd_process_protocol_error(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
bool power_change = false;
|
||||
#if PE_INFO_ENABLE
|
||||
uint8_t event_type = pd_event->event_type;
|
||||
uint8_t msg_type = pd_event->msg;
|
||||
uint8_t msg_id = pd_get_msg_hdr_id(pd_port);
|
||||
#endif
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT) {
|
||||
PE_INFO("Ignore Unknown Event\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pd_check_pe_during_hard_reset(pd_port)) {
|
||||
PE_INFO("Ignore Event during HReset\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SNK_TRANSITION_SINK:
|
||||
/* fall through */
|
||||
case PE_SRC_TRANSITION_SUPPLY: /* never recv ping for Source =.=*/
|
||||
/* fall through */
|
||||
case PE_SRC_TRANSITION_SUPPLY2:
|
||||
power_change = true;
|
||||
if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG,
|
||||
PD_CTRL_PING)) {
|
||||
PE_INFO("Ignore Ping\n");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
if (pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG,
|
||||
PD_CTRL_PING)) {
|
||||
PE_INFO("Ignore Ping\n");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case PE_SNK_READY:
|
||||
case PE_SRC_READY:
|
||||
if (pd_process_ready_protocol_error(pd_port)) {
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
ret = true;
|
||||
|
||||
if (pd_port->pe_data.during_swap) {
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
#else
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
#endif
|
||||
} else if (power_change)
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
else
|
||||
pe_transit_soft_reset_state(pd_port);
|
||||
|
||||
/*
|
||||
* event_type: PD_EVT_CTRL_MSG (1), PD_EVT_DATA_MSG (2)
|
||||
*/
|
||||
out:
|
||||
PE_INFO("PRL_ERR: %d-%d-%d\n", event_type, msg_type, msg_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool pd_process_tx_failed(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_check_pe_state_ready(pd_port) ||
|
||||
pd_check_pe_during_hard_reset(pd_port)) {
|
||||
PE_DBG("Ignore tx_failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
pe_transit_soft_reset_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_RESET_CABLE
|
||||
static inline bool pd_process_cable_ctrl_msg_accept(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
case PE_SRC_CBL_SEND_SOFT_RESET:
|
||||
vdm_put_dpm_discover_cable_event(pd_port);
|
||||
return false;
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
case PE_DFP_CBL_SEND_SOFT_RESET:
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_RESET_CABLE */
|
||||
|
||||
static inline bool pd_process_event_cable(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
#ifdef CONFIG_USB_PD_RESET_CABLE
|
||||
if (pd_event->msg == PD_CTRL_ACCEPT)
|
||||
ret = pd_process_cable_ctrl_msg_accept(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_RESET_CABLE */
|
||||
|
||||
if (!ret)
|
||||
PE_DBG("Ignore not SOP Ctrl Msg\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static void pd_copy_msg_data(struct pd_port *pd_port, uint8_t *payload,
|
||||
uint16_t count, uint8_t unit_sz)
|
||||
{
|
||||
pd_port->pd_msg_data_size = count * unit_sz;
|
||||
pd_port->pd_msg_data_count = count;
|
||||
pd_port->pd_msg_data_payload = payload;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static inline void pd_copy_msg_data_from_ext_evt(struct pd_port *pd_port,
|
||||
struct pd_msg *pd_msg)
|
||||
{
|
||||
uint32_t *payload = pd_msg->payload;
|
||||
|
||||
uint16_t *ext_hdr = (uint16_t *)payload;
|
||||
uint8_t *ext_data = (uint8_t *)(ext_hdr + 1);
|
||||
|
||||
uint16_t size = PD_EXT_HEADER_DATA_SIZE(*ext_hdr);
|
||||
|
||||
pd_copy_msg_data(pd_port, ext_data, size, 1);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
static inline void pd_copy_msg_data_from_evt(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
struct pd_msg *pd_msg = pd_event->pd_msg;
|
||||
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_DATA_MSG:
|
||||
PD_BUG_ON(pd_msg == NULL);
|
||||
pd_copy_msg_data(pd_port, (uint8_t *)pd_msg->payload,
|
||||
pd_get_msg_hdr_cnt(pd_port), sizeof(uint32_t));
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_EVT_EXT_MSG:
|
||||
PD_BUG_ON(pd_msg == NULL);
|
||||
pd_copy_msg_data_from_ext_evt(pd_port, pd_msg);
|
||||
return;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
default:
|
||||
pd_copy_msg_data(pd_port, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
*
|
||||
* @ true : valid message
|
||||
* @ false : invalid message, pe should drop the message
|
||||
*/
|
||||
|
||||
static inline bool pe_is_valid_pd_msg_id(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event,
|
||||
struct pd_msg *pd_msg)
|
||||
{
|
||||
uint8_t sop_type = pd_msg->frame_type;
|
||||
uint8_t msg_id = pd_get_msg_hdr_id(pd_port);
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->pe_state_curr == PE_BIST_TEST_DATA)
|
||||
return false;
|
||||
|
||||
if (pd_event->event_type == PD_EVT_CTRL_MSG) {
|
||||
switch (pd_event->msg) {
|
||||
/* SofReset always has a MessageID value of zero */
|
||||
case PD_CTRL_SOFT_RESET:
|
||||
if (msg_id != 0) {
|
||||
PE_INFO("Repeat soft_reset\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
PE_DBG("Discard_CRC\n");
|
||||
return true;
|
||||
|
||||
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
|
||||
case PD_CTRL_PS_RDY:
|
||||
if (pd_port->msg_id_pr_swap_last == msg_id) {
|
||||
PE_INFO("Repeat ps_rdy\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
|
||||
}
|
||||
}
|
||||
|
||||
if (pd_port->pe_data.msg_id_rx[sop_type] == msg_id) {
|
||||
PE_INFO("Repeat msg: %c:%d:%d\n",
|
||||
(pd_event->event_type == PD_EVT_CTRL_MSG) ? 'C' : 'D',
|
||||
pd_event->msg, msg_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((pd_port->pe_data.msg_id_rx[sop_type] + 2) % PD_MSG_ID_MAX) ==
|
||||
msg_id) {
|
||||
PE_INFO("Miss Msg!!!\n");
|
||||
pd_port->miss_msg = true;
|
||||
}
|
||||
|
||||
pd_port->pe_data.msg_id_rx[sop_type] = msg_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pe_is_valid_pd_msg_role(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event,
|
||||
struct pd_msg *pd_msg)
|
||||
{
|
||||
bool ret = true;
|
||||
uint8_t msg_pr, msg_dr;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_msg == NULL) /* Good-CRC */
|
||||
return true;
|
||||
|
||||
if (pd_msg->frame_type != TCPC_TX_SOP)
|
||||
return true;
|
||||
|
||||
msg_pr = PD_HEADER_PR(pd_msg->msg_hdr);
|
||||
msg_dr = PD_HEADER_DR(pd_msg->msg_hdr);
|
||||
|
||||
/*
|
||||
* The Port Power Role field of a received Message shall not be verified
|
||||
* by the receiver and no error recovery action shall be
|
||||
* taken if it is incorrect.
|
||||
*/
|
||||
|
||||
if (msg_pr == pd_port->power_role)
|
||||
PE_DBG("Wrong PR:%d\n", msg_pr);
|
||||
|
||||
/*
|
||||
* Should a Type-C Port receive a Message with the Port Data Role field
|
||||
* set to the same Data Role as its current Data Role,
|
||||
* except for the GoodCRC Message,
|
||||
* Type-C error recovery actions as defined
|
||||
* in [USBType-C 1.0] shall be performed.
|
||||
*/
|
||||
|
||||
if (msg_dr == pd_port->data_role) {
|
||||
#ifdef CONFIG_USB_PD_CHECK_DATA_ROLE
|
||||
ret = false;
|
||||
#endif
|
||||
PE_INFO("Wrong DR:%d\n", msg_dr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void pe_translate_pd_msg_event(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event,
|
||||
struct pd_msg *pd_msg)
|
||||
{
|
||||
uint16_t msg_hdr;
|
||||
|
||||
PD_BUG_ON(pd_msg == NULL);
|
||||
|
||||
msg_hdr = pd_msg->msg_hdr;
|
||||
pd_port->curr_msg_hdr = msg_hdr;
|
||||
pd_event->msg = PD_HEADER_TYPE(msg_hdr);
|
||||
|
||||
if (PD_HEADER_CNT(msg_hdr))
|
||||
pd_event->event_type = PD_EVT_DATA_MSG;
|
||||
else
|
||||
pd_event->event_type = PD_EVT_CTRL_MSG;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
if (PD_HEADER_EXT(msg_hdr))
|
||||
pd_event->event_type = PD_EVT_EXT_MSG;
|
||||
|
||||
if (pd_msg->frame_type == TCPC_TX_SOP_PRIME) {
|
||||
pd_sync_sop_prime_spec_revision(pd_port,
|
||||
PD_HEADER_REV(msg_hdr));
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
static inline uint8_t pe_get_startup_state(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool act_as_sink = true;
|
||||
uint8_t startup_state = 0xff;
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
pd_port->custom_dbgacc = false;
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
||||
|
||||
switch (pd_event->msg_sec) {
|
||||
case TYPEC_ATTACHED_DBGACC_SNK:
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
pd_port->custom_dbgacc = true;
|
||||
startup_state = PE_DBG_READY;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
||||
case TYPEC_ATTACHED_SNK:
|
||||
startup_state = PE_SNK_STARTUP;
|
||||
break;
|
||||
|
||||
case TYPEC_ATTACHED_SRC:
|
||||
act_as_sink = false;
|
||||
startup_state = PE_SRC_STARTUP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* At least > 4 for Ellisys VNDI PR_SWAP */
|
||||
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
||||
if (pd_port->error_recovery_once > 4)
|
||||
startup_state = PE_ERROR_RECOVERY_ONCE;
|
||||
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
||||
|
||||
pd_init_message_hdr(pd_port, act_as_sink);
|
||||
return startup_state;
|
||||
}
|
||||
|
||||
static inline bool pe_transit_startup_state(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
uint8_t startup_state = pe_get_startup_state(pd_port, pd_event);
|
||||
|
||||
if (startup_state == 0xff)
|
||||
return false;
|
||||
|
||||
pd_dpm_notify_pe_startup(pd_port);
|
||||
PE_TRANSIT_STATE(pd_port, startup_state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
TII_TRAP_IN_IDLE = 0,
|
||||
TII_TRANSIT_STATE = 1,
|
||||
TII_PE_RUNNING = 2,
|
||||
};
|
||||
|
||||
static inline uint8_t pe_check_trap_in_idle_state(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
switch (pd_port->pe_pd_state) {
|
||||
case PE_IDLE1:
|
||||
case PE_ERROR_RECOVERY:
|
||||
if (pd_event_pe_msg_match(pd_event, PD_PE_IDLE)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_IDLE2);
|
||||
return TII_TRANSIT_STATE;
|
||||
}
|
||||
|
||||
pd_try_put_pe_idle_event(pd_port);
|
||||
break;
|
||||
|
||||
case PE_IDLE2:
|
||||
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_ATTACHED)) {
|
||||
if (pe_transit_startup_state(pd_port, pd_event))
|
||||
return TII_TRANSIT_STATE;
|
||||
}
|
||||
|
||||
/* The original IDLE2 may trigger by PE_IDLE_TOUT */
|
||||
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED))
|
||||
pd_notify_pe_idle(pd_port);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_IDLE1);
|
||||
return TII_TRANSIT_STATE;
|
||||
}
|
||||
return TII_PE_RUNNING;
|
||||
}
|
||||
|
||||
PE_DBG("Trap in idle state, Ignore All MSG (%d:%d)\n",
|
||||
pd_event->event_type, pd_event->msg);
|
||||
return TII_TRAP_IN_IDLE;
|
||||
}
|
||||
|
||||
static inline void pe_init_curr_state(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->power_role == PD_ROLE_SINK) {
|
||||
pd_port->curr_ready_state = PE_SNK_READY;
|
||||
pd_port->curr_hreset_state = PE_SNK_HARD_RESET;
|
||||
pd_port->curr_sreset_state = PE_SNK_SEND_SOFT_RESET;
|
||||
} else {
|
||||
pd_port->curr_ready_state = PE_SRC_READY;
|
||||
pd_port->curr_hreset_state = PE_SRC_HARD_RESET;
|
||||
pd_port->curr_sreset_state = PE_SRC_SEND_SOFT_RESET;
|
||||
}
|
||||
|
||||
pd_port->curr_unsupported_msg = false;
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
if (pd_port->custom_dbgacc)
|
||||
pd_port->curr_ready_state = PE_DBG_READY;
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
||||
}
|
||||
|
||||
bool pd_process_event(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
struct pd_msg *pd_msg = pd_event->pd_msg;
|
||||
uint8_t tii = pe_check_trap_in_idle_state(pd_port, pd_event);
|
||||
int rv = 0;
|
||||
uint32_t chip_id = 0;
|
||||
|
||||
if (tii < TII_PE_RUNNING)
|
||||
return tii;
|
||||
|
||||
pe_init_curr_state(pd_port);
|
||||
|
||||
if (pd_event->event_type == PD_EVT_PD_MSG)
|
||||
pe_translate_pd_msg_event(pd_port, pd_event, pd_msg);
|
||||
|
||||
#if PE_EVT_INFO_VDM_DIS
|
||||
if (!pd_curr_is_vdm_evt(pd_port))
|
||||
#endif
|
||||
print_event(pd_port, pd_event);
|
||||
|
||||
if ((pd_event->event_type < PD_EVT_PD_MSG_END) && (pd_msg != NULL)) {
|
||||
if (!pe_is_valid_pd_msg_id(pd_port, pd_event, pd_msg))
|
||||
return false;
|
||||
|
||||
if (!pe_is_valid_pd_msg_role(pd_port, pd_event, pd_msg)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
return true;
|
||||
}
|
||||
|
||||
rv = tcpci_get_chip_id(pd_port->tcpc, &chip_id);
|
||||
if (!rv && (SC2150A_DID == chip_id) && pd_port->miss_msg) {
|
||||
pd_port->miss_msg = false;
|
||||
if (pd_port->pe_pd_state == PE_SNK_TRANSITION_SINK) {
|
||||
if (pd_event->msg != PD_CTRL_PS_RDY) {
|
||||
pd_add_miss_msg(pd_port, pd_event,
|
||||
PD_CTRL_PS_RDY);
|
||||
return false;
|
||||
}
|
||||
} else if (pd_port->pe_pd_state ==
|
||||
PE_SNK_SELECT_CAPABILITY) {
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_PS_RDY:
|
||||
pd_add_miss_msg(pd_port, pd_event,
|
||||
PD_CTRL_ACCEPT);
|
||||
break;
|
||||
case PD_DATA_SOURCE_CAP:
|
||||
pd_add_miss_msg(pd_port, pd_event,
|
||||
PD_CTRL_REJECT);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pd_copy_msg_data_from_evt(pd_port, pd_event);
|
||||
|
||||
if (pd_curr_is_vdm_evt(pd_port))
|
||||
return pd_process_event_vdm(pd_port, pd_event);
|
||||
|
||||
if (pd_event->event_type == PD_EVT_TCP_MSG)
|
||||
return pd_process_event_tcp(pd_port, pd_event);
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
if (pd_port->custom_dbgacc)
|
||||
return pd_process_event_dbg(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
||||
|
||||
if ((pd_event->event_type == PD_EVT_CTRL_MSG) &&
|
||||
(pd_event->msg != PD_CTRL_GOOD_CRC) && (pd_msg != NULL) &&
|
||||
(pd_msg->frame_type != TCPC_TX_SOP))
|
||||
return pd_process_event_cable(pd_port, pd_event);
|
||||
|
||||
if (pd_process_event_com(pd_port, pd_event))
|
||||
return true;
|
||||
|
||||
switch (pd_port->state_machine) {
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
case PE_STATE_MACHINE_DR_SWAP:
|
||||
ret = pd_process_event_drs(pd_port, pd_event);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_STATE_MACHINE_PR_SWAP:
|
||||
ret = pd_process_event_prs(pd_port, pd_event);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
case PE_STATE_MACHINE_VCONN_SWAP:
|
||||
ret = pd_process_event_vcs(pd_port, pd_event);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return true;
|
||||
|
||||
if (pd_port->power_role == PD_ROLE_SINK)
|
||||
ret = pd_process_event_snk(pd_port, pd_event);
|
||||
else
|
||||
ret = pd_process_event_src(pd_port, pd_event);
|
||||
|
||||
return ret;
|
||||
}
|
657
drivers/usb/typec/platform/external/tcpc/pd_process_evt_com.c
vendored
Normal file
657
drivers/usb/typec/platform/external/tcpc/pd_process_evt_com.c
vendored
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Policy Engine for Common
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
|
||||
/*
|
||||
* [BLOCK] DRP (dr_swap, pr_swap, vconn_swap)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
static inline bool pd_evaluate_reject_dr_swap(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA) {
|
||||
if (pd_port->power_role == PD_ROLE_DFP)
|
||||
return pd_port->dpm_caps &
|
||||
DPM_CAP_DR_SWAP_REJECT_AS_UFP;
|
||||
return pd_port->dpm_caps & DPM_CAP_DR_SWAP_REJECT_AS_DFP;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
static inline bool pd_evaluate_reject_pr_swap(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
if (pd_port->power_role == PD_ROLE_SOURCE)
|
||||
return pd_port->dpm_caps &
|
||||
DPM_CAP_PR_SWAP_REJECT_AS_SNK;
|
||||
return pd_port->dpm_caps & DPM_CAP_PR_SWAP_REJECT_AS_SRC;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
static inline bool pd_evaluate_accept_vconn_swap(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
|
||||
static inline bool pd_process_ctrl_msg_dr_swap(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
if (pd_port->pe_data.modal_operation) {
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return false;
|
||||
|
||||
if (!pd_evaluate_reject_dr_swap(pd_port)) {
|
||||
pd_port->pe_data.during_swap = false;
|
||||
pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
|
||||
|
||||
PE_TRANSIT_DATA_STATE(pd_port, PE_DRS_UFP_DFP_EVALUATE_DR_SWAP,
|
||||
PE_DRS_DFP_UFP_EVALUATE_DR_SWAP);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_REJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg_pr_swap(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
if (!pd_evaluate_reject_pr_swap(pd_port)) {
|
||||
pd_port->pe_data.during_swap = false;
|
||||
pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
|
||||
pe_transit_evaluate_pr_swap_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_REJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg_vconn_swap(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return false;
|
||||
|
||||
if (pd_evaluate_accept_vconn_swap(pd_port)) {
|
||||
pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
|
||||
PE_TRANSIT_STATE(pd_port, PE_VCS_EVALUATE_SWAP);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
|
||||
if (!pd_check_rev30(pd_port)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_REJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] BIST
|
||||
*/
|
||||
|
||||
static inline bool pd_process_data_msg_bist(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->request_v > 5000) {
|
||||
PE_INFO("bist_not_vsafe5v\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (BDO_MODE(pd_event->pd_msg->payload[0])) {
|
||||
case BDO_MODE_TEST_DATA:
|
||||
PE_DBG("bist_test\n");
|
||||
PE_TRANSIT_STATE(pd_port, PE_BIST_TEST_DATA);
|
||||
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_TEST_DATA);
|
||||
return true;
|
||||
|
||||
case BDO_MODE_CARRIER2:
|
||||
PE_DBG("bist_cm2\n");
|
||||
PE_TRANSIT_STATE(pd_port, PE_BIST_CARRIER_MODE_2);
|
||||
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
|
||||
return true;
|
||||
|
||||
default:
|
||||
#if 0
|
||||
case BDO_MODE_RECV:
|
||||
case BDO_MODE_TRANSMIT:
|
||||
case BDO_MODE_COUNTERS:
|
||||
case BDO_MODE_CARRIER0:
|
||||
case BDO_MODE_CARRIER1:
|
||||
case BDO_MODE_CARRIER3:
|
||||
case BDO_MODE_EYE:
|
||||
#endif
|
||||
PE_DBG("Unsupport BIST\n");
|
||||
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Ctrl MSG
|
||||
*/
|
||||
|
||||
static void pd_cancel_dpm_reaction(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->pe_data.dpm_reaction_id < DPM_REACTION_REJECT_CANCEL)
|
||||
dpm_reaction_clear(pd_port, pd_port->pe_data.dpm_reaction_id);
|
||||
}
|
||||
|
||||
static bool pd_process_ctrl_msg_wait_reject(struct pd_port *pd_port)
|
||||
{
|
||||
pd_cancel_dpm_reaction(pd_port);
|
||||
|
||||
if (pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP)
|
||||
pd_notify_pe_cancel_pr_swap(pd_port);
|
||||
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg_wait(struct pd_port *pd_port)
|
||||
{
|
||||
return pd_process_ctrl_msg_wait_reject(pd_port);
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
if (!pd_check_rev30(pd_port) && pd_event->msg >= PD_CTRL_PD30_START) {
|
||||
pd_event->msg = PD_CTRL_MSG_NR;
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER)
|
||||
pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags2 &
|
||||
PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC) {
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case PD_CTRL_REJECT:
|
||||
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_REJECT);
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT) {
|
||||
return pd_process_ctrl_msg_wait_reject(pd_port);
|
||||
}
|
||||
break;
|
||||
case PD_CTRL_WAIT:
|
||||
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_WAIT);
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT) {
|
||||
return pd_process_ctrl_msg_wait(pd_port);
|
||||
}
|
||||
break;
|
||||
|
||||
case PD_CTRL_SOFT_RESET:
|
||||
if (!pd_port->pe_data.during_swap &&
|
||||
!pd_check_pe_during_hard_reset(pd_port)) {
|
||||
pe_transit_soft_reset_recv_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Swap */
|
||||
case PD_CTRL_DR_SWAP:
|
||||
ret = pd_process_ctrl_msg_dr_swap(pd_port, pd_event);
|
||||
break;
|
||||
|
||||
case PD_CTRL_PR_SWAP:
|
||||
ret = pd_process_ctrl_msg_pr_swap(pd_port, pd_event);
|
||||
break;
|
||||
|
||||
case PD_CTRL_VCONN_SWAP:
|
||||
ret = pd_process_ctrl_msg_vconn_swap(pd_port, pd_event);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
|
||||
case PD_CTRL_GET_COUNTRY_CODE:
|
||||
if (pd_port->country_nr) {
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
pe_get_curr_ready_state(pd_port),
|
||||
PE_GIVE_COUNTRY_CODES);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
|
||||
|
||||
case PD_CTRL_NOT_SUPPORTED:
|
||||
pd_cancel_dpm_reaction(pd_port);
|
||||
pd_notify_tcp_event_2nd_result(pd_port,
|
||||
TCP_DPM_RET_NOT_SUPPORT);
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) {
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
} else if (pd_port->pe_data.vdm_state_timer) {
|
||||
vdm_put_pe_event(pd_port->tcpc, PD_PE_VDM_NOT_SUPPORT);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Data MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_data_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
if (!pd_check_rev30(pd_port) && pd_event->msg >= PD_DATA_PD30_START) {
|
||||
pd_event->msg = PD_DATA_MSG_NR;
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_DATA_BIST:
|
||||
if (pd_port->pe_state_curr == ready_state)
|
||||
ret = pd_process_data_msg_bist(pd_port, pd_event);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
|
||||
case PD_DATA_BAT_STATUS:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(PE_GET_BATTERY_STATUS,
|
||||
ready_state);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
|
||||
case PD_DATA_GET_COUNTRY_INFO:
|
||||
if (pd_port->country_nr) {
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
ready_state, PE_GIVE_COUNTRY_INFO);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Extend MSG
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static inline bool pd_process_ext_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
|
||||
|
||||
if (!pd_check_rev30(pd_port)) {
|
||||
pd_event->msg = PD_DATA_MSG_NR;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USB_PD_REV30_CHUNKING_BY_PE
|
||||
if (pd_port->pe_state_curr == ready_state &&
|
||||
pd_is_multi_chunk_msg(pd_port)) {
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_CHUNKING_BY_PE */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
|
||||
case PD_EXT_GET_BAT_CAP:
|
||||
if (pd_port->bat_nr) {
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(ready_state,
|
||||
PE_GIVE_BATTERY_CAP);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
|
||||
case PD_EXT_GET_BAT_STATUS:
|
||||
if (pd_port->bat_nr) {
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
ready_state, PE_GIVE_BATTERY_STATUS);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
|
||||
case PD_EXT_BAT_CAP:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(PE_GET_BATTERY_CAP,
|
||||
ready_state);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
|
||||
case PD_EXT_GET_MFR_INFO:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(ready_state,
|
||||
PE_GIVE_MANUFACTURER_INFO);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
|
||||
case PD_EXT_MFR_INFO:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(PE_GET_MANUFACTURER_INFO,
|
||||
ready_state);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
|
||||
case PD_EXT_COUNTRY_INFO:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(PE_GET_COUNTRY_INFO,
|
||||
ready_state);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
|
||||
case PD_EXT_COUNTRY_CODES:
|
||||
ret = PE_MAKE_STATE_TRANSIT_SINGLE(PE_GET_COUNTRY_CODES,
|
||||
ready_state);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
if (pd_port->pe_data.pe_state_flags2 &
|
||||
PE_STATE_FLAG_BACK_READY_IF_DPM_ACK) {
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_DPM_NOT_SUPPORT:
|
||||
if (pd_check_rev30(pd_port)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_VDM_NOT_SUPPORTED);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess HW MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_recv_hard_reset(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
|
||||
if (pd_port->pe_data.recv_hard_reset_count > PD_HARD_RESET_COUNT) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_OVER_RECV_HRESET_LIMIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
pd_port->pe_data.recv_hard_reset_count++;
|
||||
#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */
|
||||
|
||||
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
|
||||
if (pd_check_pe_during_hard_reset(pd_port))
|
||||
pd_port->pe_data.renegotiation_count++;
|
||||
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
|
||||
|
||||
pe_transit_hard_reset_recv_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_hw_msg_tx_failed(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->pe_data.renegotiation_count > PD_HARD_RESET_COUNT) {
|
||||
PE_INFO("renegotiation failed\n");
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_BACK_READY_IF_TX_FAILED) {
|
||||
pd_notify_tcp_event_2nd_result(pd_port,
|
||||
TCP_DPM_RET_NO_RESPONSE);
|
||||
pe_transit_ready_state(pd_port);
|
||||
return true;
|
||||
} else if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_HRESET_IF_TX_FAILED) {
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_hw_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_HW_RECV_HARD_RESET:
|
||||
return pd_process_recv_hard_reset(pd_port, pd_event);
|
||||
|
||||
case PD_HW_TX_FAILED:
|
||||
return pd_process_hw_msg_tx_failed(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Timer MSG
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT
|
||||
static inline bool pd_check_rx_pending(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t alert;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (tcpci_get_alert_status(tcpc, &alert))
|
||||
return false;
|
||||
|
||||
if (alert & TCPC_REG_ALERT_RX_STATUS) {
|
||||
PE_INFO("rx_pending\n");
|
||||
#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY
|
||||
pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */
|
||||
|
||||
static inline bool pd_process_timer_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
|
||||
|
||||
switch (pd_event->msg) {
|
||||
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
|
||||
case PD_TIMER_SENDER_RESPONSE:
|
||||
|
||||
#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT
|
||||
#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY
|
||||
if (pd_check_rx_pending(pd_port))
|
||||
return false;
|
||||
#else
|
||||
pd_check_rx_pending(pd_port);
|
||||
#endif /* CONFIG_USB_PD_PRINT_SYSTEM_BUSY */
|
||||
#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */
|
||||
|
||||
pd_cancel_dpm_reaction(pd_port);
|
||||
pd_notify_pe_cancel_pr_swap(pd_port);
|
||||
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_TIMEOUT);
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) {
|
||||
PE_TRANSIT_STATE(pd_port, ready_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pd_port->pe_data.pe_state_flags &
|
||||
PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT) {
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
|
||||
case PD_TIMER_BIST_CONT_MODE:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_BIST_CARRIER_MODE_2,
|
||||
ready_state))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
|
||||
case PD_TIMER_DFP_FLOW_DELAY:
|
||||
if (pd_port->pe_state_curr == ready_state &&
|
||||
pd_port->data_role == PD_ROLE_DFP) {
|
||||
dpm_reaction_set_clear(pd_port,
|
||||
DPM_REACTION_CAP_READY_ONCE,
|
||||
DPM_REACTION_DFP_FLOW_DELAY);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
|
||||
case PD_TIMER_UFP_FLOW_DELAY:
|
||||
if (pd_port->pe_state_curr == ready_state &&
|
||||
pd_port->data_role == PD_ROLE_UFP) {
|
||||
dpm_reaction_set_clear(pd_port,
|
||||
DPM_REACTION_CAP_READY_ONCE,
|
||||
DPM_REACTION_UFP_FLOW_DELAY);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
|
||||
case PD_TIMER_VCONN_STABLE:
|
||||
if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) {
|
||||
pd_set_vconn(pd_port, PD_ROLE_VCONN_ON);
|
||||
dpm_reaction_set_clear(pd_port,
|
||||
DPM_REACTION_CAP_READY_ONCE,
|
||||
DPM_REACTION_VCONN_STABLE_DELAY);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
|
||||
|
||||
#if defined(CONFIG_USB_PD_REV30) && defined(CONFIG_USB_PD_REV30_COLLISION_AVOID)
|
||||
case PD_TIMER_DEFERRED_EVT:
|
||||
pd_notify_tcp_event_buf_reset(pd_port,
|
||||
TCP_DPM_RET_DROP_PE_BUSY);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pd_process_event_com(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DATA_MSG:
|
||||
return pd_process_data_msg(pd_port, pd_event);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_EVT_EXT_MSG:
|
||||
return pd_process_ext_msg(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_HW_MSG:
|
||||
return pd_process_hw_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
return pd_process_timer_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
28
drivers/usb/typec/platform/external/tcpc/pd_process_evt_dbg.c
vendored
Normal file
28
drivers/usb/typec/platform/external/tcpc/pd_process_evt_dbg.c
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For DBGACC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
||||
|
||||
bool pd_process_event_dbg(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
/* Don't need to handle any PD message, Keep VBUS 5V, and using VDM */
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
|
101
drivers/usb/typec/platform/external/tcpc/pd_process_evt_drs.c
vendored
Normal file
101
drivers/usb/typec/platform/external/tcpc/pd_process_evt_drs.c
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For DRS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
|
||||
/* PD Control MSG reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
|
||||
{ PE_DRS_DFP_UFP_ACCEPT_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP },
|
||||
{ PE_DRS_UFP_DFP_ACCEPT_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
|
||||
{ PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP },
|
||||
{ PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
|
||||
|
||||
/* DPM Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
|
||||
{ PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_ACCEPT_DR_SWAP },
|
||||
{ PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_ACCEPT_DR_SWAP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
|
||||
{ PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_REJECT_DR_SWAP },
|
||||
{ PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_REJECT_DR_SWAP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess PD Ctrl MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
|
||||
|
||||
case PD_CTRL_ACCEPT:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
|
||||
|
||||
case PD_DPM_NAK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Process Policy Engine's DRS Message
|
||||
*/
|
||||
|
||||
bool pd_process_event_drs(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
238
drivers/usb/typec/platform/external/tcpc/pd_process_evt_prs.c
vendored
Normal file
238
drivers/usb/typec/platform/external/tcpc/pd_process_evt_prs.c
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For PRS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
|
||||
#define PE_PRS_SNK_HARD_RESET PE_ERROR_RECOVERY
|
||||
#define PE_PRS_SRC_HARD_RESET PE_ERROR_RECOVERY
|
||||
#else
|
||||
#define PE_PRS_SNK_HARD_RESET PE_SNK_HARD_RESET
|
||||
#define PE_PRS_SRC_HARD_RESET PE_SRC_HARD_RESET
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY */
|
||||
|
||||
/* PD Control MSG reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
|
||||
{ PE_PRS_SRC_SNK_ACCEPT_PR_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
|
||||
{ PE_PRS_SNK_SRC_ACCEPT_PR_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
|
||||
|
||||
/* VBUS-ON & PS_RDY SENT */
|
||||
{ PE_PRS_SNK_SRC_SOURCE_ON, PE_SRC_STARTUP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
|
||||
{ PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
|
||||
{ PE_PRS_SNK_SRC_SEND_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = {
|
||||
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP },
|
||||
{ PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY);
|
||||
|
||||
/* DPM Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
|
||||
{ PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_ACCEPT_PR_SWAP },
|
||||
{ PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_ACCEPT_PR_SWAP },
|
||||
|
||||
{ PE_PRS_SRC_SNK_ASSERT_RD, PE_PRS_SRC_SNK_WAIT_SOURCE_ON },
|
||||
{ PE_PRS_SNK_SRC_ASSERT_RP, PE_PRS_SNK_SRC_SOURCE_ON },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
|
||||
{ PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_REJECT_PR_SWAP },
|
||||
{ PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_REJECT_SWAP },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
|
||||
|
||||
/* HW Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_HW_VBUS_PRESENT) = {
|
||||
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
|
||||
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP },
|
||||
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_HW_VBUS_PRESENT);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_HW_TX_FAILED) = {
|
||||
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_PRS_SNK_HARD_RESET },
|
||||
{ PE_PRS_SNK_SRC_SOURCE_ON, PE_PRS_SRC_HARD_RESET },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_HW_TX_FAILED);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_HW_VBUS_SAFE0V) = {
|
||||
{ PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_ASSERT_RD },
|
||||
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
|
||||
{ PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP },
|
||||
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_HW_VBUS_SAFE0V);
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess PD Ctrl MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_ctrl_msg_good_crc(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_ON);
|
||||
pd_unlock_msg_output(pd_port); /* for tSRCTransition */
|
||||
return false;
|
||||
|
||||
default:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg_ps_rdy(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
|
||||
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
|
||||
pd_enable_vbus_valid_detection(pd_port, true);
|
||||
return false;
|
||||
|
||||
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
|
||||
pd_enable_vbus_safe0v_detection(pd_port);
|
||||
return false;
|
||||
|
||||
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
|
||||
default:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
|
||||
|
||||
case PD_CTRL_ACCEPT:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
|
||||
|
||||
case PD_CTRL_PS_RDY:
|
||||
return pd_process_ctrl_msg_ps_rdy(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
|
||||
|
||||
case PD_DPM_NAK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess HW MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_hw_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_HW_VBUS_PRESENT:
|
||||
if (pd_port->pe_state_curr == PE_PRS_SNK_SRC_SOURCE_ON)
|
||||
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
|
||||
|
||||
return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_PRESENT);
|
||||
|
||||
case PD_HW_TX_FAILED:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_HW_TX_FAILED);
|
||||
|
||||
case PD_HW_VBUS_SAFE0V:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_SAFE0V);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Timer MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_timer_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_TIMER_PS_SOURCE_ON:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_PRS_SNK_HARD_RESET);
|
||||
|
||||
case PD_TIMER_PS_SOURCE_OFF:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
|
||||
PE_PRS_SNK_HARD_RESET);
|
||||
|
||||
case PD_TIMER_SOURCE_TRANSITION:
|
||||
pd_dpm_prs_enable_power_source(pd_port, false);
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Process Policy Engine's PRS Message
|
||||
*/
|
||||
|
||||
bool pd_process_event_prs(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_HW_MSG:
|
||||
return pd_process_hw_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
return pd_process_timer_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
499
drivers/usb/typec/platform/external/tcpc/pd_process_evt_snk.c
vendored
Normal file
499
drivers/usb/typec/platform/external/tcpc/pd_process_evt_snk.c
vendored
Normal file
@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For SNK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/tcpci_typec.h>
|
||||
|
||||
/* PD Control MSG reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
|
||||
{ PE_SNK_SELECT_CAPABILITY, PE_SNK_TRANSITION_SINK },
|
||||
{ PE_SNK_SEND_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
|
||||
|
||||
/* PD Data MSG reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SOURCE_CAP) = {
|
||||
{ PE_SNK_WAIT_FOR_CAPABILITIES, PE_SNK_EVALUATE_CAPABILITY },
|
||||
{ PE_SNK_READY, PE_SNK_EVALUATE_CAPABILITY },
|
||||
|
||||
/* PR-Swap issue (Check it later) */
|
||||
{ PE_SNK_STARTUP, PE_SNK_EVALUATE_CAPABILITY },
|
||||
{ PE_SNK_DISCOVERY, PE_SNK_EVALUATE_CAPABILITY },
|
||||
|
||||
#ifdef CONFIG_USB_PD_TCPM_CB_2ND
|
||||
{ PE_SNK_GET_SOURCE_CAP, PE_SNK_EVALUATE_CAPABILITY },
|
||||
#endif /* CONFIG_USB_PD_TCPM_CB_2ND */
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DATA_MSG_SOURCE_CAP);
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Ctrl MSG
|
||||
*/
|
||||
|
||||
static bool pd_process_ctrl_msg_get_source_cap(struct pd_port *pd_port,
|
||||
uint8_t next)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SNK_READY)
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
PE_TRANSIT_STATE(pd_port, next);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SNK_GET_SOURCE_CAP:
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_DR_SNK_GET_SINK_CAP:
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP &&
|
||||
pd_event->msg <= PD_CTRL_VCONN_SWAP) {
|
||||
PE_DBG("Port Partner Request First\n");
|
||||
pd_port->pe_state_curr = PE_SNK_READY;
|
||||
pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SNK_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES);
|
||||
|
||||
case PD_CTRL_GOTO_MIN:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_TRANSITION_SINK))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_ACCEPT:
|
||||
if (PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_PS_RDY:
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SNK_TRANSITION_SINK:
|
||||
pd_dpm_snk_transition_power(pd_port);
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
|
||||
return true;
|
||||
|
||||
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
|
||||
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
|
||||
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
|
||||
return false;
|
||||
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PD_CTRL_GET_SOURCE_CAP:
|
||||
if (pd_process_ctrl_msg_get_source_cap(
|
||||
pd_port, PE_DR_SNK_GIVE_SOURCE_CAP))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_GET_SINK_CAP:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_GIVE_SINK_CAP))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_REJECT:
|
||||
case PD_CTRL_WAIT:
|
||||
if (pd_port->pe_state_curr == PE_SNK_SELECT_CAPABILITY) {
|
||||
if (pd_port->pe_data.explicit_contract)
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
|
||||
else {
|
||||
PE_TRANSIT_STATE(pd_port,
|
||||
PE_SNK_WAIT_FOR_CAPABILITIES);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_CTRL_NOT_SUPPORTED:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_NOT_SUPPORTED_RECEIVED))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
case PD_CTRL_GET_SOURCE_CAP_EXT:
|
||||
if (pd_process_ctrl_msg_get_source_cap(
|
||||
pd_port, PE_DR_SNK_GIVE_SOURCE_CAP_EXT))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
case PD_CTRL_GET_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_GIVE_SINK_STATUS))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Data MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_data_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DATA_SOURCE_CAP:
|
||||
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
|
||||
pd_port->msg_id_pr_swap_last = 0xff;
|
||||
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
|
||||
if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SOURCE_CAP))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PD_DATA_SINK_CAP:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_DR_SNK_GET_SINK_CAP,
|
||||
PE_SNK_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
case PD_DATA_ALERT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_SOURCE_ALERT_RECEIVED))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Extend MSG
|
||||
*/
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static inline bool pd_process_ext_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
case PD_EXT_SOURCE_CAP_EXT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_GET_SOURCE_CAP_EXT,
|
||||
PE_SNK_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
case PD_EXT_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_GET_SOURCE_STATUS,
|
||||
PE_SNK_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
case PD_EXT_PPS_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_GET_PPS_STATUS,
|
||||
PE_SNK_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_EVALUATE_CAPABILITY,
|
||||
PE_SNK_SELECT_CAPABILITY);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess HW MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_hw_msg_sink_tx_change(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
struct pe_data *pe_data = &pd_port->pe_data;
|
||||
uint8_t pd_traffic;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
|
||||
if (pe_data->pd_traffic_control == PD_SINK_TX_START)
|
||||
return false;
|
||||
#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */
|
||||
|
||||
if (!pd_check_rev30(pd_port))
|
||||
return false;
|
||||
|
||||
pd_traffic = pd_event->msg_sec ? PD_SINK_TX_OK : PD_SINK_TX_NG;
|
||||
|
||||
if (pe_data->pd_traffic_control == pd_traffic)
|
||||
return false;
|
||||
|
||||
pe_data->pd_traffic_control = pd_traffic;
|
||||
dpm_reaction_set_ready_once(pd_port);
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_vbus_absent(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SNK_DISCOVERY)
|
||||
return false;
|
||||
#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW
|
||||
/* iSafe0mA: Maximum current a Sink
|
||||
* is allowed to draw when VBUS is driven to vSafe0V
|
||||
*/
|
||||
pd_dpm_sink_vbus(pd_port, false);
|
||||
#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */
|
||||
pd_disable_pe_state_timer(pd_port);
|
||||
pd_enable_vbus_valid_detection(pd_port, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_hw_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_HW_VBUS_PRESENT:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SNK_DISCOVERY, PE_SNK_WAIT_FOR_CAPABILITIES);
|
||||
|
||||
case PD_HW_VBUS_ABSENT:
|
||||
return pd_process_vbus_absent(pd_port);
|
||||
|
||||
case PD_HW_TX_FAILED:
|
||||
return pd_process_tx_failed(pd_port);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
case PD_HW_SINK_TX_CHANGE:
|
||||
return pd_process_hw_msg_sink_tx_change(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess PE MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_pe_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_PE_RESET_PRL_COMPLETED:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_STARTUP,
|
||||
PE_SNK_DISCOVERY);
|
||||
|
||||
case PD_PE_HARD_RESET_COMPLETED:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SNK_HARD_RESET, PE_SNK_TRANSITION_TO_DEFAULT);
|
||||
|
||||
case PD_PE_POWER_ROLE_AT_DEFAULT:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SNK_TRANSITION_TO_DEFAULT, PE_SNK_STARTUP);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Timer MSG
|
||||
*/
|
||||
|
||||
static inline void pd_report_typec_only_charger(struct pd_port *pd_port)
|
||||
{
|
||||
uint8_t state;
|
||||
struct tcpc_device *tcpc = pd_port->tcpc;
|
||||
|
||||
if (tcpc->typec_remote_rp_level == TYPEC_CC_VOLT_SNK_DFT)
|
||||
state = PD_CONNECT_TYPEC_ONLY_SNK_DFT;
|
||||
else
|
||||
state = PD_CONNECT_TYPEC_ONLY_SNK;
|
||||
|
||||
PE_INFO("TYPE-C Only Charger!\n");
|
||||
pd_dpm_sink_vbus(pd_port, true);
|
||||
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
|
||||
pd_notify_pe_hard_reset_completed(pd_port);
|
||||
pd_update_connect_state(pd_port, state);
|
||||
}
|
||||
|
||||
static inline bool pd_process_timer_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
|
||||
struct pe_data __maybe_unused *pe_data = &pd_port->pe_data;
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_TIMER_SINK_REQUEST:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_READY,
|
||||
PE_SNK_SELECT_CAPABILITY);
|
||||
|
||||
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
|
||||
case PD_TIMER_SINK_WAIT_CAP:
|
||||
case PD_TIMER_PS_TRANSITION:
|
||||
if ((pd_port->pe_state_curr != PE_SNK_DISCOVERY) &&
|
||||
(pe_data->hard_reset_counter <= PD_HARD_RESET_COUNT)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET);
|
||||
return true;
|
||||
}
|
||||
#ifdef CONFIG_SUPPORT_PISEN_ADAPTER
|
||||
if ((pd_port->pe_state_curr == PE_SNK_DISCOVERY) &&
|
||||
(pe_data->retry_cnt < PD_HARD_RESET_RETRY_COUNT)) {
|
||||
pe_data->retry_cnt++;
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_SUPPORT_PISEN_ADAPTER */
|
||||
|
||||
PE_INFO("SRC NoResp\n");
|
||||
if (pd_port->request_v == TCPC_VBUS_SINK_5V) {
|
||||
pd_report_typec_only_charger(pd_port);
|
||||
} else {
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
|
||||
case PD_TIMER_DISCOVER_ID:
|
||||
vdm_put_dpm_discover_cable_event(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
|
||||
/* fall through */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_TIMER_CK_NOT_SUPPORTED:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SNK_CHUNK_RECEIVED,
|
||||
PE_SNK_SEND_NOT_SUPPORTED))
|
||||
return true;
|
||||
/* fall through */
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
|
||||
case PD_TIMER_SNK_FLOW_DELAY:
|
||||
if (pe_data->pd_traffic_control == PD_SINK_TX_START) {
|
||||
if (typec_get_cc_res() == TYPEC_CC_VOLT_SNK_3_0)
|
||||
pe_data->pd_traffic_control = PD_SINK_TX_OK;
|
||||
else
|
||||
pe_data->pd_traffic_control = PD_SINK_TX_NG;
|
||||
if (pd_check_rev30(pd_port))
|
||||
dpm_reaction_set_ready_once(pd_port);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Process Policy Engine's SNK Message
|
||||
*/
|
||||
|
||||
bool pd_process_event_snk(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DATA_MSG:
|
||||
return pd_process_data_msg(pd_port, pd_event);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_EVT_EXT_MSG:
|
||||
return pd_process_ext_msg(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_HW_MSG:
|
||||
return pd_process_hw_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_PE_MSG:
|
||||
return pd_process_pe_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
return pd_process_timer_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
538
drivers/usb/typec/platform/external/tcpc/pd_process_evt_src.c
vendored
Normal file
538
drivers/usb/typec/platform/external/tcpc/pd_process_evt_src.c
vendored
Normal file
@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For SRC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
|
||||
/* PD Data MSG reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DATA_MSG_REQUEST) = {
|
||||
{ PE_SRC_SEND_CAPABILITIES, PE_SRC_NEGOTIATE_CAPABILITIES },
|
||||
{ PE_SRC_READY, PE_SRC_NEGOTIATE_CAPABILITIES },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DATA_MSG_REQUEST);
|
||||
|
||||
/* DPM Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
|
||||
{ PE_SRC_NEGOTIATE_CAPABILITIES, PE_SRC_TRANSITION_SUPPLY },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_CAP_CHANGED) = {
|
||||
{ PE_SRC_READY, PE_SRC_SEND_CAPABILITIES },
|
||||
{ PE_SRC_WAIT_NEW_CAPABILITIES, PE_SRC_SEND_CAPABILITIES },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_CAP_CHANGED);
|
||||
|
||||
/* Timer Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_TIMER_PS_HARD_RESET) = {
|
||||
{ PE_SRC_HARD_RESET, PE_SRC_TRANSITION_TO_DEFAULT },
|
||||
{ PE_SRC_HARD_RESET_RECEIVED, PE_SRC_TRANSITION_TO_DEFAULT },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_TIMER_PS_HARD_RESET);
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Ctrl MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_ctrl_msg_good_crc(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SRC_SEND_CAPABILITIES:
|
||||
pd_port->pe_data.cap_counter = 0;
|
||||
pd_handle_hard_reset_recovery(pd_port);
|
||||
return false;
|
||||
|
||||
case PE_SRC_TRANSITION_SUPPLY:
|
||||
pd_enable_pe_state_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
|
||||
return false;
|
||||
|
||||
case PE_SRC_CAPABILITY_RESPONSE:
|
||||
if (!pd_port->pe_data.explicit_contract)
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_WAIT_NEW_CAPABILITIES);
|
||||
else if (pd_port->pe_data.invalid_contract)
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
|
||||
else
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_READY);
|
||||
return true;
|
||||
|
||||
case PE_SRC_SOFT_RESET:
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg_get_sink_cap(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SRC_READY)
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GIVE_SINK_CAP);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SRC_GET_SINK_CAP:
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_DR_SRC_GET_SOURCE_CAP:
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP &&
|
||||
pd_event->msg <= PD_CTRL_VCONN_SWAP) {
|
||||
PE_DBG("Port Partner Request First\n");
|
||||
pd_port->pe_state_curr = PE_SRC_READY;
|
||||
pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
|
||||
|
||||
case PD_CTRL_ACCEPT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_SEND_SOFT_RESET,
|
||||
PE_SRC_SEND_CAPABILITIES))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_GET_SOURCE_CAP:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_SEND_CAPABILITIES))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_GET_SINK_CAP:
|
||||
if (pd_process_ctrl_msg_get_sink_cap(pd_port, pd_event))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_CTRL_NOT_SUPPORTED:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_NOT_SUPPORTED_RECEIVED))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
||||
case PD_CTRL_GET_SOURCE_CAP_EXT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_GIVE_SOURCE_CAP_EXT))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
case PD_CTRL_GET_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_GIVE_SOURCE_STATUS))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
|
||||
case PD_CTRL_GET_PPS_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_GIVE_PPS_STATUS))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Data MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_data_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PD_DATA_SOURCE_CAP:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_DR_SRC_GET_SOURCE_CAP,
|
||||
PE_SRC_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
case PD_DATA_SINK_CAP:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_GET_SINK_CAP,
|
||||
PE_SRC_READY))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_DATA_REQUEST:
|
||||
if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_REQUEST))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
||||
case PD_DATA_ALERT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_READY,
|
||||
PE_SRC_SINK_ALERT_RECEIVED))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Extend MSG
|
||||
*/
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
static inline bool pd_process_ext_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
case PD_EXT_SOURCE_CAP_EXT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_DR_SRC_GET_SOURCE_CAP_EXT,
|
||||
PE_SRC_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
||||
case PD_EXT_STATUS:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_GET_SINK_STATUS,
|
||||
PE_SRC_READY))
|
||||
return true;
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
||||
|
||||
default:
|
||||
pd_port->curr_unsupported_msg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return pd_process_protocol_error(pd_port, pd_event);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
|
||||
|
||||
case PD_DPM_NAK:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SRC_NEGOTIATE_CAPABILITIES,
|
||||
PE_SRC_CAPABILITY_RESPONSE);
|
||||
|
||||
case PD_DPM_CAP_CHANGED:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_CAP_CHANGED);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess HW MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_hw_msg_vbus_present(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SRC_STARTUP:
|
||||
pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
|
||||
break;
|
||||
|
||||
case PE_SRC_TRANSITION_TO_DEFAULT:
|
||||
pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool pd_process_hw_msg_tx_failed(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
struct pe_data *pe_data = &pd_port->pe_data;
|
||||
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
|
||||
|
||||
if (pd_port->pe_state_curr == PE_SRC_SEND_CAPABILITIES) {
|
||||
if (!pe_data->pd_connected || !pe_data->explicit_contract) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_DISCOVERY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
if (pd_port->pe_state_curr == PE_SRC_CBL_SEND_SOFT_RESET) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
|
||||
return pd_process_tx_failed(pd_port);
|
||||
}
|
||||
|
||||
static inline bool pd_process_hw_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_HW_VBUS_PRESENT:
|
||||
return pd_process_hw_msg_vbus_present(pd_port, pd_event);
|
||||
|
||||
case PD_HW_VBUS_SAFE0V:
|
||||
pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER);
|
||||
return false;
|
||||
|
||||
case PD_HW_VBUS_STABLE:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_TRANSITION_SUPPLY,
|
||||
PE_SRC_TRANSITION_SUPPLY2);
|
||||
|
||||
case PD_HW_TX_FAILED:
|
||||
return pd_process_hw_msg_tx_failed(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess PE MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_pe_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_PE_RESET_PRL_COMPLETED:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_STARTUP,
|
||||
PE_SRC_SEND_CAPABILITIES);
|
||||
|
||||
case PD_PE_POWER_ROLE_AT_DEFAULT:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(
|
||||
PE_SRC_TRANSITION_TO_DEFAULT, PE_SRC_STARTUP);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Timer MSG
|
||||
*/
|
||||
static inline bool pd_process_timer_msg_source_start(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
|
||||
if (pd_is_discover_cable(pd_port) &&
|
||||
pd_port->pe_data.msg_id_tx[TCPC_TX_SOP_PRIME] == 0) {
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
if (pd_is_reset_cable(pd_port)) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_CBL_SEND_SOFT_RESET);
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
|
||||
if (vdm_put_dpm_discover_cable_event(pd_port))
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
|
||||
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SRC_STARTUP:
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
case PE_SRC_CBL_SEND_SOFT_RESET:
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
static inline bool pd_process_timer_msg_source_cap(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SRC_DISCOVERY)
|
||||
return false;
|
||||
|
||||
if (pd_port->pe_data.cap_counter <= PD_CAPS_COUNT)
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
|
||||
else /* in this state, PD always not connected */
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_timer_msg_no_response(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
if (pd_port->pe_data.hard_reset_counter <= PD_HARD_RESET_COUNT)
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
|
||||
else if (pd_port->pe_data.pd_prev_connected)
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
else
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool pd_process_timer_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_TIMER_SOURCE_CAPABILITY:
|
||||
return pd_process_timer_msg_source_cap(pd_port, pd_event);
|
||||
|
||||
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
|
||||
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
||||
case PD_TIMER_SENDER_RESPONSE:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_CBL_SEND_SOFT_RESET,
|
||||
PE_SRC_SEND_CAPABILITIES);
|
||||
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
||||
#endif
|
||||
|
||||
case PD_TIMER_PS_HARD_RESET:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_HARD_RESET);
|
||||
|
||||
case PD_TIMER_SOURCE_START:
|
||||
return pd_process_timer_msg_source_start(pd_port, pd_event);
|
||||
|
||||
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
|
||||
case PD_TIMER_NO_RESPONSE:
|
||||
return pd_process_timer_msg_no_response(pd_port, pd_event);
|
||||
#endif
|
||||
|
||||
case PD_TIMER_SOURCE_TRANSITION:
|
||||
if (pd_port->state_machine != PE_STATE_MACHINE_PR_SWAP)
|
||||
pd_dpm_src_transition_power(pd_port);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PD_DISCOVER_CABLE_ID
|
||||
case PD_TIMER_DISCOVER_ID:
|
||||
vdm_put_dpm_discover_cable_event(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_PD_DISCOVER_CABLE_ID */
|
||||
|
||||
case PD_TIMER_SRC_RECOVER:
|
||||
pd_dpm_source_vbus(pd_port, true);
|
||||
pd_enable_vbus_valid_detection(pd_port, true);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
|
||||
case PD_TIMER_SINK_TX:
|
||||
if (pd_port->pe_data.pd_traffic_control == PD_SINK_TX_NG)
|
||||
pd_port->pe_data.pd_traffic_control = PD_SOURCE_TX_OK;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP
|
||||
if (pd_port->pe_data.pd_traffic_control == PD_SOURCE_TX_START)
|
||||
pd_port->pe_data.pd_traffic_control = PD_SINK_TX_OK;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP */
|
||||
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
|
||||
/* fall through */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_TIMER_CK_NOT_SUPPORTED:
|
||||
return PE_MAKE_STATE_TRANSIT_SINGLE(PE_SRC_CHUNK_RECEIVED,
|
||||
PE_SRC_SEND_NOT_SUPPORTED);
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Process Policy Engine's SRC Message
|
||||
*/
|
||||
|
||||
bool pd_process_event_src(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DATA_MSG:
|
||||
return pd_process_data_msg(pd_port, pd_event);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_EVT_EXT_MSG:
|
||||
return pd_process_ext_msg(pd_port, pd_event);
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_HW_MSG:
|
||||
return pd_process_hw_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_PE_MSG:
|
||||
return pd_process_pe_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
return pd_process_timer_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
469
drivers/usb/typec/platform/external/tcpc/pd_process_evt_tcp.c
vendored
Normal file
469
drivers/usb/typec/platform/external/tcpc/pd_process_evt_tcp.c
vendored
Normal file
@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event for TCP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
#include <linux/usb/tcpc/pd_dpm_core.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
static inline int pd_handle_tcp_event_pr_swap(struct pd_port *pd_port,
|
||||
uint8_t new_role)
|
||||
{
|
||||
if (pd_port->power_role == new_role)
|
||||
return TCP_DPM_RET_DENIED_SAME_ROLE;
|
||||
|
||||
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER))
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
pd_port->pe_data.during_swap = false;
|
||||
pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
|
||||
|
||||
pe_transit_send_pr_swap_state(pd_port);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
static inline int pd_handle_tcp_event_dr_swap(struct pd_port *pd_port,
|
||||
uint8_t new_role)
|
||||
{
|
||||
if (pd_port->data_role == new_role)
|
||||
return TCP_DPM_RET_DENIED_SAME_ROLE;
|
||||
|
||||
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA))
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
pd_port->pe_data.during_swap = false;
|
||||
pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
|
||||
|
||||
PE_TRANSIT_DATA_STATE(pd_port, PE_DRS_UFP_DFP_SEND_DR_SWAP,
|
||||
PE_DRS_DFP_UFP_SEND_DR_SWAP);
|
||||
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
static inline int pd_handle_tcp_event_vconn_swap(struct pd_port *pd_port,
|
||||
uint8_t new_role)
|
||||
{
|
||||
uint8_t old_role = pd_port->vconn_role ? 1 : 0;
|
||||
|
||||
if (old_role == new_role)
|
||||
return TCP_DPM_RET_DENIED_SAME_ROLE;
|
||||
|
||||
if ((!pd_port->vconn_role) &&
|
||||
(!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)))
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
|
||||
PE_TRANSIT_STATE(pd_port, PE_VCS_SEND_SWAP);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
|
||||
#ifdef CONFIG_USB_PD_PE_SOURCE
|
||||
static inline int pd_handle_tcp_event_gotomin(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SRC_READY)
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
if (!(pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_GIVE_BACK))
|
||||
return TCP_DPM_RET_DENIED_PARTNER_CAP;
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_TRANSITION_SUPPLY);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PE_SOURCE */
|
||||
|
||||
static inline int pd_handle_tcp_event_softreset(struct pd_port *pd_port)
|
||||
{
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
pe_transit_soft_reset_state(pd_port);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
static inline int pd_handle_tcp_event_cable_softreset(struct pd_port *pd_port)
|
||||
{
|
||||
bool role_check;
|
||||
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
role_check = pd_port->data_role == PD_ROLE_DFP;
|
||||
|
||||
if (pd_check_rev30(pd_port))
|
||||
role_check = pd_port->vconn_role;
|
||||
|
||||
if (!role_check)
|
||||
return TCP_DPM_RET_DENIED_WRONG_DATA_ROLE;
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_DFP_CBL_SEND_SOFT_RESET);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
|
||||
static inline int pd_handle_tcp_event_get_source_cap(struct pd_port *pd_port)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SNK_READY:
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP);
|
||||
return TCP_DPM_RET_SENT;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_SRC_READY:
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GET_SOURCE_CAP);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
}
|
||||
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
}
|
||||
|
||||
static inline int pd_handle_tcp_event_get_sink_cap(struct pd_port *pd_port)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SRC_READY:
|
||||
PE_TRANSIT_STATE(pd_port, PE_SRC_GET_SINK_CAP);
|
||||
return TCP_DPM_RET_SENT;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_SNK_READY:
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_DR_SNK_GET_SINK_CAP);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
}
|
||||
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_PE_SINK
|
||||
static inline int pd_handle_tcp_event_request(struct pd_port *pd_port)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tcp_dpm_event *tcp_event = &pd_port->tcp_event;
|
||||
|
||||
if (pd_port->pe_state_curr != PE_SNK_READY)
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
switch (pd_get_curr_pd_event(pd_port)->msg) {
|
||||
case TCP_DPM_EVT_REQUEST:
|
||||
ret = pd_dpm_update_tcp_request(
|
||||
pd_port, &tcp_event->tcp_dpm_data.pd_req);
|
||||
break;
|
||||
case TCP_DPM_EVT_REQUEST_EX:
|
||||
ret = pd_dpm_update_tcp_request_ex(
|
||||
pd_port, &tcp_event->tcp_dpm_data.pd_req_ex);
|
||||
break;
|
||||
case TCP_DPM_EVT_REQUEST_AGAIN:
|
||||
ret = pd_dpm_update_tcp_request_again(pd_port);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != TCP_DPM_RET_SUCCESS)
|
||||
return ret;
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_SELECT_CAPABILITY);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PE_SINK */
|
||||
|
||||
static inline int pd_handle_tcp_event_bist_cm2(struct pd_port *pd_port)
|
||||
{
|
||||
uint32_t bist = BDO_MODE_CARRIER2;
|
||||
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
pd_send_sop_data_msg(pd_port, PD_DATA_BIST, 1, &bist);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
static inline int
|
||||
pd_handle_tcp_event_get_source_cap_ext(struct pd_port *pd_port)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
case PE_SNK_READY:
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP_EXT);
|
||||
return TCP_DPM_RET_SENT;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
case PE_SRC_READY:
|
||||
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
|
||||
PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GET_SOURCE_CAP_EXT);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
return TCP_DPM_RET_DENIED_LOCAL_CAP;
|
||||
}
|
||||
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
static inline int pd_handle_tcp_event_get_pps_status(struct pd_port *pd_port)
|
||||
{
|
||||
if (pd_port->pe_state_curr != PE_SNK_READY)
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_PPS_STATUS);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
static inline int pd_make_tcp_event_transit_ready(struct pd_port *pd_port,
|
||||
uint8_t state)
|
||||
{
|
||||
if (!pd_check_pe_state_ready(pd_port))
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
|
||||
PE_TRANSIT_STATE(pd_port, state);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
|
||||
static inline int pd_make_tcp_event_transit_ready2(struct pd_port *pd_port,
|
||||
uint8_t snk_state,
|
||||
uint8_t src_state)
|
||||
{
|
||||
switch (pd_port->pe_state_curr) {
|
||||
#ifdef CONFIG_USB_PD_PE_SINK
|
||||
case PE_SNK_READY:
|
||||
PE_TRANSIT_STATE(pd_port, snk_state);
|
||||
return TCP_DPM_RET_SENT;
|
||||
#endif /* CONFIG_USB_PD_PE_SINK */
|
||||
|
||||
#ifdef CONFIG_USB_PD_PE_SOURCE
|
||||
case PE_SRC_READY:
|
||||
PE_TRANSIT_STATE(pd_port, src_state);
|
||||
return TCP_DPM_RET_SENT;
|
||||
#endif /* CONFIG_USB_PD_PE_SOURCE */
|
||||
}
|
||||
|
||||
return TCP_DPM_RET_DENIED_NOT_READY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
static inline int pd_handle_tcp_event_alert(struct pd_port *pd_port)
|
||||
{
|
||||
struct tcp_dpm_event *tcp_event = &pd_port->tcp_event;
|
||||
|
||||
if (pd_get_curr_pd_event(pd_port)->msg_sec == PD_TCP_FROM_TCPM)
|
||||
pd_port->pe_data.local_alert |= tcp_event->tcp_dpm_data.index;
|
||||
|
||||
return pd_make_tcp_event_transit_ready2(pd_port, PE_SNK_SEND_SINK_ALERT,
|
||||
PE_SRC_SEND_SOURCE_ALERT);
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
|
||||
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
static inline int pd_handle_tcp_event_hardreset(struct pd_port *pd_port)
|
||||
{
|
||||
pe_transit_hard_reset_state(pd_port);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
|
||||
static inline int pd_handle_tcp_event_error_recovery(struct pd_port *pd_port)
|
||||
{
|
||||
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
|
||||
return TCP_DPM_RET_SENT;
|
||||
}
|
||||
|
||||
static inline int pd_handle_tcp_dpm_event(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
int ret = TCP_DPM_RET_DENIED_UNKNOWN;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
if (pd_event->msg >= TCP_DPM_EVT_PD30_COMMAND &&
|
||||
pd_event->msg < TCP_DPM_EVT_VDM_COMMAND) {
|
||||
if (!pd_check_rev30(pd_port))
|
||||
return TCP_DPM_RET_DENIED_PD_REV;
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
switch (pd_event->msg) {
|
||||
default:
|
||||
break;
|
||||
case TCP_DPM_EVT_PR_SWAP_AS_SNK:
|
||||
case TCP_DPM_EVT_PR_SWAP_AS_SRC:
|
||||
#ifdef CONFIG_USB_PD_PR_SWAP
|
||||
ret = pd_handle_tcp_event_pr_swap(
|
||||
pd_port, pd_event->msg - TCP_DPM_EVT_PR_SWAP_AS_SNK);
|
||||
#endif /* CONFIG_USB_PD_PR_SWAP */
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_DR_SWAP_AS_UFP:
|
||||
case TCP_DPM_EVT_DR_SWAP_AS_DFP:
|
||||
#ifdef CONFIG_USB_PD_DR_SWAP
|
||||
ret = pd_handle_tcp_event_dr_swap(
|
||||
pd_port, pd_event->msg - TCP_DPM_EVT_DR_SWAP_AS_UFP);
|
||||
#endif /* CONFIG_USB_PD_DR_SWAP */
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_VCONN_SWAP_OFF:
|
||||
case TCP_DPM_EVT_VCONN_SWAP_ON:
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
ret = pd_handle_tcp_event_vconn_swap(
|
||||
pd_port, pd_event->msg - TCP_DPM_EVT_VCONN_SWAP_OFF);
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_GOTOMIN:
|
||||
#ifdef CONFIG_USB_PD_PE_SOURCE
|
||||
ret = pd_handle_tcp_event_gotomin(pd_port);
|
||||
#endif /* CONFIG_USB_PD_PE_SOURCE */
|
||||
break;
|
||||
case TCP_DPM_EVT_SOFTRESET:
|
||||
ret = pd_handle_tcp_event_softreset(pd_port);
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_CABLE_SOFTRESET:
|
||||
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
||||
ret = pd_handle_tcp_event_cable_softreset(pd_port);
|
||||
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_GET_SOURCE_CAP:
|
||||
ret = pd_handle_tcp_event_get_source_cap(pd_port);
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_GET_SINK_CAP:
|
||||
ret = pd_handle_tcp_event_get_sink_cap(pd_port);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_PE_SINK
|
||||
case TCP_DPM_EVT_REQUEST:
|
||||
ret = pd_handle_tcp_event_request(pd_port);
|
||||
break;
|
||||
case TCP_DPM_EVT_REQUEST_EX:
|
||||
ret = pd_handle_tcp_event_request(pd_port);
|
||||
break;
|
||||
case TCP_DPM_EVT_REQUEST_AGAIN:
|
||||
ret = pd_handle_tcp_event_request(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_PE_SINK */
|
||||
|
||||
case TCP_DPM_EVT_BIST_CM2:
|
||||
ret = pd_handle_tcp_event_bist_cm2(pd_port);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
||||
case TCP_DPM_EVT_GET_SOURCE_CAP_EXT:
|
||||
ret = pd_handle_tcp_event_get_source_cap_ext(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
||||
case TCP_DPM_EVT_GET_STATUS:
|
||||
ret = pd_make_tcp_event_transit_ready2(pd_port,
|
||||
PE_SNK_GET_SOURCE_STATUS,
|
||||
PE_SRC_GET_SINK_STATUS);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
|
||||
case TCP_DPM_EVT_GET_COUNTRY_CODE:
|
||||
ret = pd_make_tcp_event_transit_ready(pd_port,
|
||||
PE_GET_COUNTRY_CODES);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
||||
case TCP_DPM_EVT_GET_PPS_STATUS:
|
||||
ret = pd_handle_tcp_event_get_pps_status(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
||||
case TCP_DPM_EVT_ALERT:
|
||||
ret = pd_handle_tcp_event_alert(pd_port);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
|
||||
case TCP_DPM_EVT_GET_COUNTRY_INFO:
|
||||
ret = pd_make_tcp_event_transit_ready(pd_port,
|
||||
PE_GET_COUNTRY_INFO);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
|
||||
case TCP_DPM_EVT_GET_BAT_CAP:
|
||||
ret = pd_make_tcp_event_transit_ready(pd_port,
|
||||
PE_GET_BATTERY_CAP);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
|
||||
case TCP_DPM_EVT_GET_BAT_STATUS:
|
||||
ret = pd_make_tcp_event_transit_ready(pd_port,
|
||||
PE_GET_BATTERY_STATUS);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
|
||||
case TCP_DPM_EVT_GET_MFRS_INFO:
|
||||
ret = pd_make_tcp_event_transit_ready(pd_port,
|
||||
PE_GET_MANUFACTURER_INFO);
|
||||
break;
|
||||
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
|
||||
#endif /* CONFIG_USB_PD_REV30 */
|
||||
|
||||
case TCP_DPM_EVT_HARD_RESET:
|
||||
ret = pd_handle_tcp_event_hardreset(pd_port);
|
||||
break;
|
||||
|
||||
case TCP_DPM_EVT_ERROR_RECOVERY:
|
||||
ret = pd_handle_tcp_event_error_recovery(pd_port);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool pd_process_event_tcp(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
int ret = pd_handle_tcp_dpm_event(pd_port, pd_event);
|
||||
|
||||
pd_notify_tcp_event_1st_result(pd_port, ret);
|
||||
return ret == TCP_DPM_RET_SENT;
|
||||
}
|
125
drivers/usb/typec/platform/external/tcpc/pd_process_evt_vcs.c
vendored
Normal file
125
drivers/usb/typec/platform/external/tcpc/pd_process_evt_vcs.c
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Richtek Inc.
|
||||
*
|
||||
* Power Delivery Process Event For VCS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb/tcpc/pd_core.h>
|
||||
#include <linux/usb/tcpc/tcpci_event.h>
|
||||
#include <linux/usb/tcpc/pd_process_evt.h>
|
||||
|
||||
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
||||
/* DPM Event reactions */
|
||||
|
||||
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
|
||||
{ PE_VCS_EVALUATE_SWAP, PE_VCS_ACCEPT_SWAP },
|
||||
{ PE_VCS_TURN_ON_VCONN, PE_VCS_SEND_PS_RDY },
|
||||
};
|
||||
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess PD Ctrl MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_ctrl_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
uint8_t vconn_state = pd_port->vconn_role ? PE_VCS_WAIT_FOR_VCONN :
|
||||
PE_VCS_TURN_ON_VCONN;
|
||||
|
||||
switch (pd_event->msg) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_VCS_ACCEPT_SWAP,
|
||||
vconn_state))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_ACCEPT:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_VCS_SEND_SWAP, vconn_state))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PD_CTRL_PS_RDY:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_VCS_WAIT_FOR_VCONN,
|
||||
PE_VCS_TURN_OFF_VCONN))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess DPM MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_dpm_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_DPM_ACK:
|
||||
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
|
||||
|
||||
case PD_DPM_NAK:
|
||||
if (PE_MAKE_STATE_TRANSIT_SINGLE(PE_VCS_EVALUATE_SWAP,
|
||||
PE_VCS_REJECT_VCONN_SWAP))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Porcess Timer MSG
|
||||
*/
|
||||
|
||||
static inline bool pd_process_timer_msg(struct pd_port *pd_port,
|
||||
struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->msg) {
|
||||
case PD_TIMER_VCONN_ON:
|
||||
if (PE_MAKE_STATE_TRANSIT_TO_HRESET(PE_VCS_WAIT_FOR_VCONN))
|
||||
return true;
|
||||
break;
|
||||
|
||||
#if CONFIG_USB_PD_VCONN_READY_TOUT != 0
|
||||
case PD_TIMER_VCONN_READY:
|
||||
PE_STATE_DPM_ACK_IMMEDIATELY(pd_port);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* [BLOCK] Process Policy Engine's VCS Message
|
||||
*/
|
||||
|
||||
bool pd_process_event_vcs(struct pd_port *pd_port, struct pd_event *pd_event)
|
||||
{
|
||||
switch (pd_event->event_type) {
|
||||
case PD_EVT_CTRL_MSG:
|
||||
return pd_process_ctrl_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_DPM_MSG:
|
||||
return pd_process_dpm_msg(pd_port, pd_event);
|
||||
|
||||
case PD_EVT_TIMER_MSG:
|
||||
return pd_process_timer_msg(pd_port, pd_event);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user