drivers: thermal: MAX31760: Maxim Fan Controller Driver

This adds support for MAX31760 Fan Controller.
Fan is enabled and its speed can be controlled.

Change-Id: Ibe0d67f4d9699bcf3bfa58e034743bb8263dda6c
Signed-off-by: Minghao Zhang <quic_minghao@quicinc.com>
This commit is contained in:
Minghao Zhang 2022-08-30 18:26:18 +08:00
parent e05bd7deb5
commit 5115d35e98
3 changed files with 338 additions and 0 deletions

View File

@ -175,6 +175,15 @@ config QTI_DDR_COOLING_DEVICE
to meet the performance requirement under thermally constrained
conditions.
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_THERMALZONE_CONFIG_DEBUG
tristate "QTI ThermalZone config debug driver"
depends on THERMAL && THERMAL_OF

View File

@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o
obj-$(CONFIG_QTI_RPM_SMD_COOLING_DEVICE) += rpm_smd_cooling_device.o
obj-$(CONFIG_QTI_LIMITS_ISENSE_CDSP) += msm_isense_cdsp.o
obj-$(CONFIG_QTI_DDR_COOLING_DEVICE) += ddr_cdev.o
obj-$(CONFIG_MAX31760_FAN_CONTROLLER) += max31760_fan.o
obj-$(CONFIG_QTI_USERSPACE_CDEV) += qti_userspace_cdev.o
obj-$(CONFIG_QTI_CPUFREQ_CDEV) += qti_cpufreq_cdev.o
obj-$(CONFIG_QTI_DEVFREQ_CDEV) += qti_devfreq_cdev.o

View File

@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 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 VDD_MAX_UV 3100000
#define VDD_MIN_UV 3000000
#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_LEVEL4 4
#define FAN_SPEED_MAX 5
struct max31760_data {
struct device *dev;
struct i2c_client *i2c_client;
struct thermal_cooling_device *cdev;
struct mutex update_lock;
u32 pwr_en_gpio;
u32 driver_en_gpio;
unsigned int cur_state;
atomic_t in_suspend;
};
static int max31760_speed_map[FAN_SPEED_MAX] = {0x00, 0x30, 0x85, 0xCF, 0xFF};
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);
gpio_direction_output(pdata->driver_en_gpio, on);
dev_dbg(pdata->dev, "max31760 gpio:%d and gpio:%d set to %d\n", pdata->pwr_en_gpio,
pdata->driver_en_gpio, on);
usleep_range(20000, 20100);
}
static void max31760_speed_control(struct max31760_data *pdata, unsigned long level)
{
max31760_write_byte(pdata, MAX31760_DUTY_CYCLE_CTRL_REG, max31760_speed_map[level]);
}
static void max31760_set_cur_state_common(struct max31760_data *pdata,
unsigned long state)
{
if (state > FAN_SPEED_LEVEL4)
state = FAN_SPEED_LEVEL4;
if (state < FAN_SPEED_LEVEL0)
state = FAN_SPEED_LEVEL0;
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_LEVEL4;
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;
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 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);
max31760_write_byte(pdata, MAX31760_CTRL_REG3, 0x31);
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;
}
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, "max31760 enable gpio not specified\n");
return -EINVAL;
}
pdata->driver_en_gpio = of_get_named_gpio(node, "maxim,driver-en-gpio", 0);
if (!gpio_is_valid(pdata->driver_en_gpio)) {
dev_err(pdata->dev, "max31760 drvr enable gpio not specified\n");
return -EINVAL;
}
ret = gpio_request(pdata->pwr_en_gpio, "pwr_en_gpio");
if (ret) {
pr_err("max31760 enable gpio request failed, ret:%d\n", ret);
goto error;
}
ret = gpio_request(pdata->driver_en_gpio, "driver_en_gpio");
if (ret) {
pr_err("max31760 drvr enable gpio request failed, ret:%d\n", ret);
goto error;
}
max31760_enable_gpio(pdata, 1);
return ret;
error:
gpio_free(pdata->pwr_en_gpio);
gpio_free(pdata->driver_en_gpio);
return ret;
}
static int max31760_remove(struct i2c_client *client)
{
struct max31760_data *pdata = i2c_get_clientdata(client);
if (!pdata)
return 0;
thermal_cooling_device_unregister(pdata->cdev);
max31760_enable_gpio(pdata, 0);
gpio_free(pdata->pwr_en_gpio);
gpio_free(pdata->driver_en_gpio);
return 0;
}
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;
}
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;
}
return ret;
fail_register_cdev:
max31760_remove(client);
return ret;
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);
mutex_unlock(&pdata->update_lock);
}
return 0;
}
static int max31760_resume(struct device *dev)
{
struct max31760_data *pdata = dev_get_drvdata(dev);
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);
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 v2");