Merge "defconfig: arm64: Enable MAX31760 Fan Controller for PineappleP HDK"

This commit is contained in:
qctecmdr 2023-08-14 16:34:15 -07:00 committed by Gerrit - the friendly Code Review server
commit e52926740d
5 changed files with 580 additions and 0 deletions

View File

@ -69,6 +69,7 @@ CONFIG_IPC_LOG_MINIDUMP_BUFFERS=16
CONFIG_LEDS_QTI_FLASH=m
CONFIG_LEDS_QTI_TRI_LED=m
CONFIG_MAC80211=m
CONFIG_MAX31760_FAN_CONTROLLER=m
CONFIG_MEM_SHARE_QMI_SERVICE=m
CONFIG_MFD_I2C_PMIC=m
CONFIG_MFD_SPMI_PMIC=m

View File

@ -158,6 +158,15 @@ config QTI_USERSPACE_CDEV
socket, so that userspace cooling device can perform a mitigation
action.
config MAX31760_FAN_CONTROLLER
tristate "MAX31760 Fan Controller Driver"
depends on THERMAL && THERMAL_OF
help
This enables the MAX31760 fan controller cooling device.
This driver uses i2c bus to control registers for setting
different fan speed. Also, this driver initializes the power for
the fan controller.
config QTI_THERMAL_LIMITS_DCVS
tristate "QTI LMH DCVS Driver"
depends on THERMAL && CPU_THERMAL

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_QTI_CPU_VOLTAGE_COOLING_DEVICE) += cpu_voltage_cooling.o
obj-$(CONFIG_QTI_CPU_HOTPLUG_COOLING_DEVICE) += cpu_hotplug.o
obj-$(CONFIG_QTI_DDR_COOLING_DEVICE) += ddr_cdev.o
obj-$(CONFIG_QTI_USERSPACE_CDEV) += qti_userspace_cdev.o
obj-$(CONFIG_MAX31760_FAN_CONTROLLER) += max31760_fan.o
obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o
obj-$(CONFIG_QTI_THERMALZONE_CONFIG_DEBUG) += thermal_config.o
obj-${CONFIG_QTI_SDPM_CLOCK_MONITOR} += sdpm_clk.o

View File

@ -0,0 +1,568 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/thermal.h>
#define MAX31760_CTRL_REG1 0x00
#define MAX31760_CTRL_REG2 0x01
#define MAX31760_CTRL_REG3 0x02
#define MAX31760_DUTY_CYCLE_CTRL_REG 0x50
#define MAX31760_TC1H_REG 0x52
#define MAX31760_TC1L_REG 0x53
#define MAX31760_TC2H_REG 0x54
#define MAX31760_TC2L_REG 0x55
#define VDD_MAX_UV 3300000
#define VDD_MIN_UV 3296000
#define VDD_LOAD_UA 300000
#define VCCA_MAX_UV 1800000
#define VCCA_MIN_UV 1800000
#define VCCA_LOAD_UA 600000
#define FAN_SPEED_LEVEL0 0
#define FAN_SPEED_MAX 100
#define SPEED_CAL_CONST (60 * 100000)
#define MSB_CONVERT_DEC (256)
#define PWM_FACTOR 39
struct max31760_data {
struct device *dev;
struct i2c_client *i2c_client;
struct thermal_cooling_device *cdev;
struct mutex update_lock;
struct regulator *vdd_reg;
struct regulator *vcca_reg;
u32 fan_num;
u32 pwr_en_gpio;
unsigned int cur_state;
atomic_t in_suspend;
};
static int max31760_read_byte(struct max31760_data *pdata, u8 reg, u8 *val)
{
int ret;
struct i2c_client *client = pdata->i2c_client;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(pdata->dev, "failed read reg 0x%02x failure, ret:%d\n", reg, ret);
*val = (u8)ret;
dev_dbg(pdata->dev, "success read reg 0x%x=0x%x\n", reg, *val);
return ret;
}
static int max31760_write_byte(struct max31760_data *pdata, u8 reg, u8 val)
{
int ret = 0;
struct i2c_client *client = pdata->i2c_client;
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0) {
dev_err(pdata->dev, "failed write reg %#x failure, ret:%d\n", reg, ret);
return ret;
}
dev_dbg(pdata->dev, "successfully write reg %#x=%#x\n", reg, val);
return 0;
}
static void max31760_enable_gpio(struct max31760_data *pdata, int on)
{
gpio_direction_output(pdata->pwr_en_gpio, on);
dev_dbg(pdata->dev, "max31760 gpio:%d set to %d\n", pdata->pwr_en_gpio, on);
usleep_range(20000, 20100);
}
static void max31760_speed_control(struct max31760_data *pdata, unsigned long level)
{
unsigned long data;
data = level * 255 / 100;
max31760_write_byte(pdata, MAX31760_DUTY_CYCLE_CTRL_REG, data);
}
static void max31760_set_cur_state_common(struct max31760_data *pdata,
unsigned long state)
{
if (!atomic_read(&pdata->in_suspend))
max31760_speed_control(pdata, state);
pdata->cur_state = state;
}
static int max31760_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = FAN_SPEED_MAX;
return 0;
}
static int max31760_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct max31760_data *data = cdev->devdata;
mutex_lock(&data->update_lock);
*state = data->cur_state;
mutex_unlock(&data->update_lock);
return 0;
}
static int max31760_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct max31760_data *data = cdev->devdata;
if (state > FAN_SPEED_MAX) {
dev_err(data->dev, "fail to set current state\n");
return -EINVAL;
}
mutex_lock(&data->update_lock);
max31760_set_cur_state_common(data, state);
mutex_unlock(&data->update_lock);
return 0;
}
static struct thermal_cooling_device_ops max31760_cooling_ops = {
.get_max_state = max31760_get_max_state,
.get_cur_state = max31760_get_cur_state,
.set_cur_state = max31760_set_cur_state,
};
static ssize_t speed_control_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max31760_data *data = dev_get_drvdata(dev);
int ret;
if (!data) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
ret = scnprintf(buf, PAGE_SIZE, "%d\n", data->cur_state);
return ret;
}
static ssize_t speed_control_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct max31760_data *data = dev_get_drvdata(dev);
unsigned long value;
if (kstrtoul(buf, 0, &value))
return -EINVAL;
mutex_lock(&data->update_lock);
max31760_set_cur_state_common(data, value);
mutex_unlock(&data->update_lock);
return count;
}
static int max31760_read_speed(struct max31760_data *data,
u8 index, u32 *speed)
{
u8 tch = 0, tcl = 0;
u8 value = 0;
int ret = 0;
if (index == 1) {
ret = max31760_read_byte(data, MAX31760_TC1H_REG, &value);
if (ret < 0)
return ret;
tch = value;
ret = max31760_read_byte(data, MAX31760_TC1L_REG, &value);
if (ret < 0)
return ret;
tcl = value;
} else if (index == 2) {
ret = max31760_read_byte(data, MAX31760_TC2H_REG, &value);
if (ret < 0)
return ret;
tch = value;
ret = max31760_read_byte(data, MAX31760_TC2L_REG, &value);
if (ret < 0)
return ret;
tcl = value;
}
*speed = SPEED_CAL_CONST / (tch * MSB_CONVERT_DEC + tcl) / 2;
return 0;
}
static ssize_t speed_tc1_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max31760_data *data = dev_get_drvdata(dev);
u32 speed;
int ret;
if (!data) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
ret = max31760_read_speed(data, 1, &speed);
if (ret < 0) {
dev_err(data->dev, "can not read fan speed\n");
return -EINVAL;
}
ret = scnprintf(buf, PAGE_SIZE, "%d\n", speed);
dev_dbg(data->dev, "TC1 current speed is %d\n", speed);
return ret;
}
static ssize_t speed_tc2_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max31760_data *data = dev_get_drvdata(dev);
u32 speed;
int ret;
if (!data) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
ret = max31760_read_speed(data, 2, &speed);
if (ret < 0) {
dev_err(data->dev, "can not read fan speed\n");
return -EINVAL;
}
ret = scnprintf(buf, PAGE_SIZE, "%d\n", speed);
dev_dbg(data->dev, "TC2 current speed is %d\n", speed);
return ret;
}
static ssize_t pwm_duty_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max31760_data *data = dev_get_drvdata(dev);
u32 duty;
u8 value = 0;
int ret;
if (!data) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
ret = max31760_read_byte(data, MAX31760_DUTY_CYCLE_CTRL_REG, &value);
if (ret < 0)
return ret;
duty = value * PWM_FACTOR;
ret = scnprintf(buf, PAGE_SIZE, "%d\n", duty);
return ret;
}
static DEVICE_ATTR_RW(speed_control);
static DEVICE_ATTR_RO(speed_tc1);
static DEVICE_ATTR_RO(speed_tc2);
static DEVICE_ATTR_RO(pwm_duty);
static struct attribute *max31760_sysfs_attrs[] = {
&dev_attr_speed_control.attr,
&dev_attr_speed_tc1.attr,
&dev_attr_speed_tc2.attr,
&dev_attr_pwm_duty.attr,
NULL,
};
static int max31760_register_cdev(struct max31760_data *pdata)
{
int ret = 0;
char cdev_name[THERMAL_NAME_LENGTH] = "";
snprintf(cdev_name, THERMAL_NAME_LENGTH, "fan-max31760");
pdata->cdev = thermal_of_cooling_device_register(pdata->dev->of_node, cdev_name,
pdata, &max31760_cooling_ops);
if (IS_ERR(pdata->cdev)) {
ret = PTR_ERR(pdata->cdev);
dev_err(pdata->dev, "Cooling register failed for %s, ret:%d\n", cdev_name, ret);
pdata->cdev = NULL;
return ret;
}
dev_dbg(pdata->dev, "Cooling register success for %s\n", cdev_name);
return 0;
}
static void max31760_hw_init(struct max31760_data *pdata)
{
max31760_write_byte(pdata, MAX31760_CTRL_REG1, 0x19);
max31760_write_byte(pdata, MAX31760_CTRL_REG2, 0x11);
if (pdata->fan_num == 1)
max31760_write_byte(pdata, MAX31760_CTRL_REG3, 0x31);
else if (pdata->fan_num == 2)
max31760_write_byte(pdata, MAX31760_CTRL_REG3, 0x33);
mutex_lock(&pdata->update_lock);
max31760_speed_control(pdata, FAN_SPEED_LEVEL0);
pdata->cur_state = FAN_SPEED_LEVEL0;
mutex_unlock(&pdata->update_lock);
atomic_set(&pdata->in_suspend, 0);
}
static int max31760_parse_dt(struct max31760_data *pdata)
{
int ret = 0;
struct device_node *node = pdata->dev->of_node;
if (!node) {
pr_err("device tree info missing\n");
return -EINVAL;
}
ret = of_property_read_u32(node, "maxim,fan-num", &pdata->fan_num);
if (ret)
pdata->fan_num = 1;
if (pdata->fan_num > 2)
pdata->fan_num = 2;
pdata->pwr_en_gpio = of_get_named_gpio(node, "maxim,pwr-en-gpio", 0);
if (!gpio_is_valid(pdata->pwr_en_gpio)) {
dev_err(pdata->dev, "enable gpio not specified\n");
return -EINVAL;
}
ret = gpio_request(pdata->pwr_en_gpio, "pwr_en_gpio");
if (ret) {
pr_err("enable gpio request failed, ret:%d\n", ret);
goto error;
}
max31760_enable_gpio(pdata, 1);
return ret;
error:
gpio_free(pdata->pwr_en_gpio);
return ret;
}
static struct attribute_group max31760_attribute_group = {
.attrs = max31760_sysfs_attrs,
};
static int max31760_enable_vregs(struct max31760_data *pdata)
{
int ret = 0;
pdata->vdd_reg = devm_regulator_get(pdata->dev, "maxim,vdd");
if (IS_ERR(pdata->vdd_reg)) {
ret = PTR_ERR(pdata->vdd_reg);
dev_err(pdata->dev, "couldn't get vdd_reg regulator, ret:%d\n", ret);
pdata->vdd_reg = NULL;
return ret;
}
regulator_set_voltage(pdata->vdd_reg, VDD_MIN_UV, VDD_MAX_UV);
regulator_set_load(pdata->vdd_reg, VDD_LOAD_UA);
ret = regulator_enable(pdata->vdd_reg);
if (ret < 0) {
dev_err(pdata->dev, "vdd_reg regulator failed, ret:%d\n", ret);
regulator_set_voltage(pdata->vdd_reg, 0, VDD_MAX_UV);
regulator_set_load(pdata->vdd_reg, 0);
return -EINVAL;
}
pdata->vcca_reg = devm_regulator_get(pdata->dev, "maxim,vcca");
if (IS_ERR(pdata->vcca_reg)) {
ret = PTR_ERR(pdata->vcca_reg);
dev_err(pdata->dev, "couldn't get vcca_reg regulator, ret:%d\n", ret);
pdata->vcca_reg = NULL;
return ret;
}
regulator_set_voltage(pdata->vcca_reg, VCCA_MIN_UV, VCCA_MAX_UV);
regulator_set_load(pdata->vcca_reg, VCCA_LOAD_UA);
ret = regulator_enable(pdata->vcca_reg);
if (ret < 0) {
dev_err(pdata->dev, "vcca_reg regulator failed, ret:%d\n", ret);
regulator_set_voltage(pdata->vcca_reg, 0, VCCA_MAX_UV);
regulator_set_load(pdata->vcca_reg, 0);
return -EINVAL;
}
return 0;
}
static void max31760_remove(struct i2c_client *client)
{
struct max31760_data *pdata = i2c_get_clientdata(client);
if (!pdata)
return;
thermal_cooling_device_unregister(pdata->cdev);
regulator_disable(pdata->vdd_reg);
regulator_disable(pdata->vcca_reg);
max31760_enable_gpio(pdata, 0);
gpio_free(pdata->pwr_en_gpio);
}
static int max31760_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct max31760_data *pdata;
if (!client || !client->dev.of_node) {
pr_err("max31760 invalid input\n");
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "device doesn't support I2C\n");
return -ENODEV;
}
pdata = devm_kzalloc(&client->dev, sizeof(struct max31760_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->dev = &client->dev;
pdata->i2c_client = client;
i2c_set_clientdata(client, pdata);
dev_set_drvdata(&client->dev, pdata);
mutex_init(&pdata->update_lock);
ret = max31760_parse_dt(pdata);
if (ret) {
dev_err(pdata->dev, "failed to parse device tree, ret:%d\n", ret);
goto fail_parse_dt;
}
ret = max31760_enable_vregs(pdata);
if (ret) {
dev_err(pdata->dev, "failed to enable regulators, ret:%d\n", ret);
goto fail_enable_vregs;
}
max31760_hw_init(pdata);
ret = max31760_register_cdev(pdata);
if (ret) {
dev_err(pdata->dev, "failed to register cooling device, ret:%d\n", ret);
goto fail_register_cdev;
}
ret = devm_device_add_group(&client->dev, &max31760_attribute_group);
if (ret < 0) {
dev_err(pdata->dev, "couldn't register sysfs group\n");
return ret;
}
return ret;
fail_register_cdev:
max31760_remove(client);
return ret;
fail_enable_vregs:
max31760_enable_gpio(pdata, 0);
gpio_free(pdata->pwr_en_gpio);
fail_parse_dt:
i2c_set_clientdata(client, NULL);
dev_set_drvdata(&client->dev, NULL);
return ret;
}
static void max31760_shutdown(struct i2c_client *client)
{
max31760_remove(client);
}
static int max31760_suspend(struct device *dev)
{
struct max31760_data *pdata = dev_get_drvdata(dev);
dev_dbg(dev, "enter suspend now\n");
if (pdata) {
atomic_set(&pdata->in_suspend, 1);
mutex_lock(&pdata->update_lock);
max31760_speed_control(pdata, FAN_SPEED_LEVEL0);
max31760_enable_gpio(pdata, 0);
regulator_disable(pdata->vdd_reg);
mutex_unlock(&pdata->update_lock);
}
return 0;
}
static int max31760_resume(struct device *dev)
{
struct max31760_data *pdata = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "enter resume now\n");
if (pdata) {
atomic_set(&pdata->in_suspend, 0);
mutex_lock(&pdata->update_lock);
max31760_enable_gpio(pdata, 1);
ret = regulator_enable(pdata->vdd_reg);
if (ret < 0)
dev_err(pdata->dev, "vdd_reg regulator failed, ret:%d\n", ret);
max31760_write_byte(pdata, MAX31760_CTRL_REG1, 0x19);
max31760_write_byte(pdata, MAX31760_CTRL_REG2, 0x11);
max31760_write_byte(pdata, MAX31760_CTRL_REG3, 0x31);
max31760_set_cur_state_common(pdata, pdata->cur_state);
mutex_unlock(&pdata->update_lock);
}
return 0;
}
static const struct of_device_id max31760_id_table[] = {
{ .compatible = "maxim,max31760",},
{ },
};
static const struct i2c_device_id max31760_i2c_table[] = {
{ "max31760", 0 },
{ },
};
static SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend, max31760_resume);
static struct i2c_driver max31760_i2c_driver = {
.probe = max31760_probe,
.remove = max31760_remove,
.shutdown = max31760_shutdown,
.driver = {
.name = "max31760",
.of_match_table = max31760_id_table,
.pm = &max31760_pm_ops,
},
.id_table = max31760_i2c_table,
};
module_i2c_driver(max31760_i2c_driver);
MODULE_DEVICE_TABLE(i2c, max31760_i2c_table);
MODULE_DESCRIPTION("Maxim 31760 Fan Controller");
MODULE_LICENSE("GPL");

View File

@ -216,6 +216,7 @@ def define_pineapple():
"drivers/thermal/qcom/cpu_hotplug.ko",
"drivers/thermal/qcom/cpu_voltage_cooling.ko",
"drivers/thermal/qcom/ddr_cdev.ko",
"drivers/thermal/qcom/max31760_fan.ko",
"drivers/thermal/qcom/msm_lmh_dcvs.ko",
"drivers/thermal/qcom/qcom-spmi-temp-alarm.ko",
"drivers/thermal/qcom/qcom_tsens.ko",