Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52e66621 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "defconfig: add Maxim Fan controller"

parents f0a45fca d2f4002f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -286,6 +286,7 @@ CONFIG_UID_SYS_STATS=y
CONFIG_OKL4_USER_VIRQ=y
CONFIG_WIGIG_SENSING_SPI=m
CONFIG_QTI_XR_SMRTVWR_MISC=y
CONFIG_QTI_MAXIM_FAN_CONTROLLER=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
+1 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@ CONFIG_UID_SYS_STATS=y
CONFIG_OKL4_USER_VIRQ=y
CONFIG_WIGIG_SENSING_SPI=m
CONFIG_QTI_XR_SMRTVWR_MISC=y
CONFIG_QTI_MAXIM_FAN_CONTROLLER=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
+9 −0
Original line number Diff line number Diff line
@@ -604,6 +604,15 @@ config QTI_XR_SMRTVWR_MISC
	  driver initializes gpios, enables/disables LDOs that are part of
	  XR standalone reference device.

config QTI_MAXIM_FAN_CONTROLLER
	tristate "QTI MAXIM fan controller  driver support"
	help
	  This driver supports the Maxim(MAX31760) fan controller.
	  This driver exposes i2c control to control registers for
	  setting different PWM, different temperature settings etc.
	  Also, this driver initializes the power for the fan controller
	  and exposes sysfs node to control different speeds of fan.

source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -72,5 +72,6 @@ obj-$(CONFIG_OKL4_USER_VIPC) += okl4-vipc.o
obj-$(CONFIG_OKL4_GUEST)        += okl4-panic.o
obj-$(CONFIG_OKL4_LINK_SHBUF)    += okl4-link-shbuf.o
obj-$(CONFIG_WIGIG_SENSING_SPI)	+= wigig_sensing.o
obj-$(CONFIG_QTI_MAXIM_FAN_CONTROLLER) += max31760.o
obj-$(CONFIG_QTI_XR_SMRTVWR_MISC) += qxr-stdalonevwr.o
obj-$(CONFIG_FPR_FPC)		+= fpr_FingerprintCard/
+374 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

/*
 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
 */

#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/kernel.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/rwlock.h>
#include <linux/uaccess.h>
#include <linux/regmap.h>

struct max31760 {
	struct device *dev;
	u8 i2c_addr;
	struct regmap *regmap;
	u32 fan_pwr_en;
	u32 fan_pwr_bp;
	struct i2c_client *i2c_client;
	int pwm;
	bool fan_off;
};

static void turn_gpio(struct max31760 *pdata, bool on)
{
	if (on) {
		gpio_direction_output(pdata->fan_pwr_en, 0);
		gpio_set_value(pdata->fan_pwr_en, 1);
		pr_debug("%s gpio:%d set to high\n", __func__,
					pdata->fan_pwr_en);
		msleep(20);
		gpio_direction_output(pdata->fan_pwr_bp, 0);
		gpio_set_value(pdata->fan_pwr_bp, 1);
		pr_debug("%s gpio:%d set to high\n", __func__,
					pdata->fan_pwr_bp);
		msleep(20);
	} else {
		gpio_direction_output(pdata->fan_pwr_en, 1);
		gpio_set_value(pdata->fan_pwr_en, 0);
		pr_debug("%s gpio:%d set to low\n", __func__,
					pdata->fan_pwr_en);
		msleep(20);
		gpio_direction_output(pdata->fan_pwr_bp, 1);
		gpio_set_value(pdata->fan_pwr_bp, 0);
		pr_debug("%s gpio:%d set to low\n", __func__,
					pdata->fan_pwr_bp);
		msleep(20);
	}
}

static int max31760_i2c_reg_get(struct max31760 *pdata,
				u8 reg)
{
	int ret;
	u32 val1;

	pr_debug("%s, reg:%x\n", __func__, reg);
	ret = regmap_read(pdata->regmap, (unsigned int)reg, &val1);
	if (ret < 0) {
		pr_err("%s failed reading reg 0x%02x failure\n", __func__, reg);
		return ret;
	}

	pr_debug("%s success reading reg 0x%x=0x%x, val1=%x\n",
					 __func__, reg, val1, val1);

	return 0;
}

static int max31760_i2c_reg_set(struct max31760 *pdata,
					u8 reg, u8 val)
{
	int ret;
	int i;

	for (i = 0; i < 10; i++) {
		ret = regmap_write(pdata->regmap, reg, val);
		if (ret >= 0)
			return ret;
		msleep(20);
	}
	if (ret < 0)
		pr_err("%s loop:%d failed to write reg 0x%02x=0x%02x\n",
			 __func__, i, reg, val);
	return ret;
}

static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
				char *buf)
{
	struct max31760 *pdata;
	int ret;

	pdata =  dev_get_drvdata(dev);
	if (!pdata) {
		pr_err("invalid driver pointer\n");
		return -ENODEV;
	}

	if (pdata->fan_off)
		ret = scnprintf(buf, PAGE_SIZE, "off\n");
	else
		ret = scnprintf(buf, PAGE_SIZE, "0x%x\n", pdata->pwm);

	return ret;
}

static ssize_t fan_store(struct device *dev, struct device_attribute *attr,
				const char *buf, size_t count)
{
	long val;
	struct max31760 *pdata;

	pdata =  dev_get_drvdata(dev);
	if (!pdata) {
		pr_err("invalid driver pointer\n");
		return -ENODEV;
	}

	kstrtol(buf, 0, &val);
	pr_debug("%s, count:%d  val:%lx, buf:%s\n",
				 __func__, count, val, buf);

	if (val == 0xff) {
		turn_gpio(pdata, false);
		pdata->fan_off = true;
	} else if (val == 0xfe) {
		pdata->fan_off = false;
		turn_gpio(pdata, true);
		max31760_i2c_reg_set(pdata, 0x00, pdata->pwm);
	} else {
		max31760_i2c_reg_set(pdata, 0x00, (int)val);
		pdata->pwm = (int)val;
	}

	return count;
}

static DEVICE_ATTR_RW(fan);

static struct attribute *max31760_fs_attrs[] = {
	&dev_attr_fan.attr,
	NULL
};

static struct attribute_group max31760_fs_attr_group = {
	.attrs = max31760_fs_attrs,
};

static int max31760_parse_dt(struct device *dev,
				struct max31760 *pdata)
{
	struct device_node *np = dev->of_node;
	int ret;

	pdata->fan_pwr_en =
		of_get_named_gpio(np, "qcom,fan-pwr-en", 0);
	if (!gpio_is_valid(pdata->fan_pwr_en)) {
		pr_err("%s fan_pwr_en gpio not specified\n", __func__);
		ret = -EINVAL;
	} else {
		ret = gpio_request(pdata->fan_pwr_en, "fan_pwr_en");
		if (ret) {
			pr_err("max31760 fan_pwr_en gpio request failed\n");
			goto error1;
		}
	}

	pdata->fan_pwr_bp =
		of_get_named_gpio(np, "qcom,fan-pwr-bp", 0);
	if (!gpio_is_valid(pdata->fan_pwr_bp)) {
		pr_err("%s fan_pwr_bp gpio not specified\n", __func__);
		ret = -EINVAL;
	} else
		ret = gpio_request(pdata->fan_pwr_bp, "fan_pwr_bp");
		if (ret) {
			pr_err("max31760 fan_pwr_bp gpio request failed\n");
			goto error2;
	}
	turn_gpio(pdata, true);

	return ret;

error2:
	gpio_free(pdata->fan_pwr_bp);
error1:
	gpio_free(pdata->fan_pwr_en);
	return ret;
}

static int max31760_fan_pwr_enable_vregs(struct device *dev,
				 struct max31760 *pdata)
{
	int ret;
	struct regulator *reg;

	/* Fan Control LDO L10A */
	reg = devm_regulator_get(dev, "pm8150_l10");
	if (!IS_ERR(reg)) {
		regulator_set_load(reg, 600000);
		ret = regulator_enable(reg);
		if (ret < 0) {
			pr_err("%s pm8150_l10 failed\n", __func__);
			return -EINVAL;
		}
	}

	/* Fan Control LDO S4 */
	reg = devm_regulator_get(dev, "pm8150_s4");
	if (!IS_ERR(reg)) {
		regulator_set_load(reg, 600000);
		ret = regulator_enable(reg);
		if (ret < 0) {
			pr_err("%s pm8150_s4 failed\n", __func__);
			return -EINVAL;
		}
	}

	return ret;
}

static const struct regmap_config max31760_regmap = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = 0xFF,
};

static int max31760_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	int ret;
	struct max31760 *pdata;

	if (!client || !client->dev.of_node) {
		pr_err("%s invalid input\n", __func__);
		return -EINVAL;
	}

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		pr_err("%s device doesn't support I2C\n", __func__);
		return -ENODEV;
	}

	pdata = devm_kzalloc(&client->dev,
		sizeof(struct max31760), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	pdata->regmap = devm_regmap_init_i2c(client, &max31760_regmap);
	if (IS_ERR(pdata->regmap)) {
		ret = PTR_ERR(pdata->regmap);
		pr_err("%s Failed to allocate regmap: %d\n", __func__, ret);
		return -EINVAL;
	}

	ret = max31760_parse_dt(&client->dev, pdata);
	if (ret) {
		pr_err("%s failed to parse device tree\n", __func__);
		return -EINVAL;
	}

	ret = max31760_fan_pwr_enable_vregs(&client->dev, pdata);
	if (ret) {
		pr_err("%s failed to pwr regulators\n", __func__);
		return -EINVAL;
	}

	pdata->dev = &client->dev;
	i2c_set_clientdata(client, pdata);

	pdata->i2c_client = client;

	dev_set_drvdata(&client->dev, pdata);

	ret = sysfs_create_group(&pdata->dev->kobj, &max31760_fs_attr_group);
	if (ret)
		pr_err("%s unable to register max31760 sysfs nodes\n");

	/* 00 - 0x01 -- 33Hz */
	/* 01 - 0x09 -- 150Hz */
	/* 10 - 0x11 -- 1500Hz */
	/* 11 - 0x19 -- 25Khz */
	pdata->pwm = 0x19;
	max31760_i2c_reg_set(pdata, 0x00, pdata->pwm);
	max31760_i2c_reg_set(pdata, 0x01, 0x11);
	max31760_i2c_reg_set(pdata, 0x02, 0x31);
	max31760_i2c_reg_set(pdata, 0x03, 0x45);
	max31760_i2c_reg_set(pdata, 0x04, 0xff);
	max31760_i2c_reg_set(pdata, 0x50, 0xcf);
	max31760_i2c_reg_set(pdata, 0x01, 0x11);
	max31760_i2c_reg_set(pdata, 0x00, pdata->pwm);
	max31760_i2c_reg_get(pdata, 0x00);

	return ret;
}

static int max31760_remove(struct i2c_client *client)
{
	struct max31760 *pdata = i2c_get_clientdata(client);

	if (!pdata)
		goto end;

	sysfs_remove_group(&pdata->dev->kobj, &max31760_fs_attr_group);
	turn_gpio(pdata, false);
end:
	return 0;
}


static void max31760_shutdown(struct i2c_client *client)
{
}

static int max31760_suspend(struct device *dev, pm_message_t state)
{
	struct max31760 *pdata =  dev_get_drvdata(dev);

	dev_dbg(dev, "suspend\n");
	if (pdata)
		turn_gpio(pdata, false);
	return 0;
}

static int max31760_resume(struct device *dev)
{
	struct max31760 *pdata =  dev_get_drvdata(dev);

	dev_dbg(dev, "resume\n");
	if (pdata) {
		turn_gpio(pdata, true);
		max31760_i2c_reg_set(pdata, 0x00, pdata->pwm);
	}
	return 0;
}

static const struct of_device_id max31760_id_table[] = {
	{ .compatible = "maxim,xrfancontroller",},
	{ },
};
static const struct i2c_device_id max31760_i2c_table[] = {
	{ "xrfancontroller", 0 },
	{ },
};

static struct i2c_driver max31760_i2c_driver = {
	.probe = max31760_probe,
	.remove = max31760_remove,
	.shutdown = max31760_shutdown,
	.driver = {
		.name = "maxim xrfancontroller",
		.of_match_table = max31760_id_table,
		.suspend = max31760_suspend,
		.resume = max31760_resume,
	},
	.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");