kernel-brax3-ubuntu-touch/drivers/thermal/mediatek/charger_cooling.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

328 lines
9.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/thermal.h>
#include <linux/types.h>
#include "charger_cooling.h"
struct charger_cooler_info {
int num_state;
int cur_current;
int cur_state;
};
static struct charger_cooler_info charger_cl_data;
/* < -1 is unlimit, unit is uA. */
static const int master_charger_state_to_current_limit[CHARGER_STATE_NUM] = {
-1, 2600000, 2200000, 1800000, 1600000, 1200000, 800000, 500000, 0
};
static const int slave_charger_state_to_current_limit[CHARGER_STATE_NUM] = {
-1, 1800000, 1600000, 1400000, 1200000, 1000000, 700000, 500000, 0
};
/*==================================================
* cooler callback functions
*==================================================
*/
static int charger_throttle(struct charger_cooling_device *charger_cdev, unsigned long state)
{
struct device *dev = charger_cdev->dev;
charger_cdev->target_state = state;
charger_cdev->pdata->state_to_charger_limit(charger_cdev);
charger_cl_data.cur_state = state;
charger_cl_data.cur_current = master_charger_state_to_current_limit[state];
dev_info(dev, "%s: set lv = %ld done\n", charger_cdev->name, state);
return 0;
}
static int charger_cooling_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
struct charger_cooling_device *charger_cdev = cdev->devdata;
*state = charger_cdev->max_state;
return 0;
}
static int charger_cooling_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
struct charger_cooling_device *charger_cdev = cdev->devdata;
*state = charger_cdev->target_state;
return 0;
}
static int charger_cooling_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
struct charger_cooling_device *charger_cdev = cdev->devdata;
int ret;
/* Request state should be less than max_state */
if (WARN_ON(state > charger_cdev->max_state || !charger_cdev->throttle))
return -EINVAL;
if (charger_cdev->target_state == state)
return 0;
ret = charger_cdev->throttle(charger_cdev, state);
return ret;
}
/*==================================================
* platform data and platform driver callbacks
*==================================================
*/
static int cooling_state_to_charger_limit_v1(struct charger_cooling_device *chg)
{
union power_supply_propval prop_bat_chr;
union power_supply_propval prop_input;
union power_supply_propval prop_vbus;
union power_supply_propval prop_s_bat_chr;
int ret = -1;
if (chg->chg_psy == NULL || IS_ERR(chg->chg_psy)) {
pr_info("Couldn't get chg_psy\n");
return ret;
}
prop_bat_chr.intval = master_charger_state_to_current_limit[chg->target_state];
ret = power_supply_set_property(chg->chg_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&prop_bat_chr);
if (ret != 0) {
pr_notice("set bat curr fail\n");
return ret;
}
if (prop_bat_chr.intval == 0)
prop_input.intval = 0;
else
prop_input.intval = -1;
ret = power_supply_set_property(chg->chg_psy,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &prop_input);
if (ret != 0) {
pr_notice("set input curr fail\n");
return ret;
}
/* High Voltage (Vbus) control*/
/*Only master charger need to control Vbus*/
/*prop.intval = 0, vbus 5V*/
/*prop.intval = 1, vbus 9V*/
prop_vbus.intval = 1;
if (prop_bat_chr.intval == 0)
prop_vbus.intval = 0;
ret = power_supply_set_property(chg->chg_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop_vbus);
if (ret != 0) {
pr_notice("set vbus fail\n");
return ret;
}
pr_notice("chr limit state %lu, chr %d, input %d, vbus %d\n",
chg->target_state, prop_bat_chr.intval, prop_input.intval, prop_vbus.intval);
power_supply_changed(chg->chg_psy);
if (chg->type == DUAL_CHARGER) {
if (chg->s_chg_psy == NULL || IS_ERR(chg->s_chg_psy)) {
pr_info("Couldn't get s_chg_psy\n");
return ret;
}
prop_s_bat_chr.intval = slave_charger_state_to_current_limit[chg->target_state];
ret = power_supply_set_property(chg->s_chg_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&prop_s_bat_chr);
if (ret != 0) {
pr_notice("set slave bat curr fail\n");
return ret;
}
power_supply_changed(chg->s_chg_psy);
}
return ret;
}
static const struct charger_cooling_platform_data mt6360_pdata = {
.state_to_charger_limit = cooling_state_to_charger_limit_v1,
};
static const struct charger_cooling_platform_data mt6375_pdata = {
.state_to_charger_limit = cooling_state_to_charger_limit_v1,
};
static const struct of_device_id charger_cooling_of_match[] = {
{
.compatible = "mediatek,mt6360-charger-cooler",
.data = (void *)&mt6360_pdata,
},
{
.compatible = "mediatek,mt6375-charger-cooler",
.data = (void *)&mt6375_pdata,
},
{},
};
MODULE_DEVICE_TABLE(of, charger_cooling_of_match);
static struct thermal_cooling_device_ops charger_cooling_ops = {
.get_max_state = charger_cooling_get_max_state,
.get_cur_state = charger_cooling_get_cur_state,
.set_cur_state = charger_cooling_set_cur_state,
};
/*return 0:single charger*/
/*return 1,2:dual charger*/
static int get_charger_type(void)
{
struct device_node *node = NULL;
u32 val = 0;
node = of_find_compatible_node(NULL, NULL, "mediatek,charger");
WARN_ON_ONCE(node == 0);
if (of_property_read_u32(node, "charger_configuration", &val))
return 0;
else
return val;
}
static ssize_t charger_curr_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int len = 0;
len += snprintf(buf + len, PAGE_SIZE - len, "%d\n",
charger_cl_data.cur_current);
return len;
}
static ssize_t charger_table_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i;
int len = 0;
for (i = 0; i < CHARGER_STATE_NUM; i++) {
if (i == 0)
len += snprintf(buf + len, PAGE_SIZE - len, "[%d,",
-1);
else if (i == CHARGER_STATE_NUM - 1)
len += snprintf(buf + len, PAGE_SIZE - len, "%d]\n",
master_charger_state_to_current_limit[i]/1000);
else
len += snprintf(buf + len, PAGE_SIZE - len, "%d,",
master_charger_state_to_current_limit[i]/1000);
}
return len;
}
static struct kobj_attribute charger_curr_attr = __ATTR_RO(charger_curr);
static struct kobj_attribute charger_table_attr = __ATTR_RO(charger_table);
static struct attribute *charger_cooler_attrs[] = {
&charger_curr_attr.attr,
&charger_table_attr.attr,
NULL
};
static struct attribute_group charger_cooler_attr_group = {
.name = "charger_cooler",
.attrs = charger_cooler_attrs,
};
static int charger_cooling_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct thermal_cooling_device *cdev;
struct charger_cooling_device *charger_cdev;
int ret, len;
charger_cdev = devm_kzalloc(dev, sizeof(*charger_cdev), GFP_KERNEL);
if (!charger_cdev)
return -ENOMEM;
charger_cdev->pdata = of_device_get_match_data(dev);
len = (strlen(np->name) > (MAX_CHARGER_COOLER_NAME_LEN - 1)) ?
(MAX_CHARGER_COOLER_NAME_LEN - 1) : strlen(np->name);
strncpy(charger_cdev->name, np->name, len);
charger_cdev->target_state = CHARGER_COOLING_UNLIMITED_STATE;
charger_cdev->dev = dev;
charger_cdev->max_state = CHARGER_STATE_NUM - 1;
charger_cdev->throttle = charger_throttle;
charger_cdev->pdata = of_device_get_match_data(dev);
charger_cdev->type = get_charger_type();
charger_cdev->chg_psy = power_supply_get_by_name("mtk-master-charger");
if (charger_cdev->chg_psy == NULL || IS_ERR(charger_cdev->chg_psy)) {
pr_info("Couldn't get chg_psy\n");
return -EINVAL;
}
if (charger_cdev->type == DUAL_CHARGER) {
charger_cdev->s_chg_psy = power_supply_get_by_name("mtk-slave-charger");
if (charger_cdev->s_chg_psy == NULL || IS_ERR(charger_cdev->s_chg_psy)) {
pr_info("Couldn't get s_chg_psy\n");
return -EINVAL;
}
}
ret = sysfs_create_group(kernel_kobj, &charger_cooler_attr_group);
if (ret) {
dev_info(&pdev->dev, "failed to create charger cooler sysfs, ret=%d!\n", ret);
return ret;
}
charger_cl_data.cur_state = 0;
charger_cl_data.cur_current = -1;
charger_cl_data.num_state = CHARGER_STATE_NUM;
cdev = thermal_of_cooling_device_register(np, charger_cdev->name,
charger_cdev, &charger_cooling_ops);
if (IS_ERR(cdev))
return -EINVAL;
charger_cdev->cdev = cdev;
platform_set_drvdata(pdev, charger_cdev);
dev_info(dev, "register %s done, id=%d\n", charger_cdev->name);
return 0;
}
static int charger_cooling_remove(struct platform_device *pdev)
{
struct charger_cooling_device *charger_cdev;
charger_cdev = (struct charger_cooling_device *)platform_get_drvdata(pdev);
thermal_cooling_device_unregister(charger_cdev->cdev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver charger_cooling_driver = {
.probe = charger_cooling_probe,
.remove = charger_cooling_remove,
.driver = {
.name = "mtk-charger-cooling",
.of_match_table = charger_cooling_of_match,
},
};
module_platform_driver(charger_cooling_driver);
MODULE_AUTHOR("Henry Huang <henry.huang@mediatek.com>");
MODULE_DESCRIPTION("Mediatek charger cooling driver");
MODULE_LICENSE("GPL v2");