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

Commit d2056c0d authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: qca1530: Sysfs control and optional power regulator"

parents 5d9a7d78 57a023cc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ Required properties

Optional properties
- qca,pwr-supply: handle to the regulator device tree node
- qca,pwr2-supply: handle to additional regulator device tree node
- qca,pwr-gpio: handle the power control using GPIO interface
- qca,reset-gpio: GPIO for reset control
- qca,reset-supply: handle to the regulator for reset control
+288 −91
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>

/* RTC clock name for lookup */
#define QCA1530_RTC_CLK_ID		"qca,rtc_clk"
@@ -32,6 +35,8 @@
#define QCA1530_TCXO_CLK_ID		"qca,tcxo_clk"
/* SoC power regulator prefix for DTS */
#define QCA1530_OF_PWR_REG_NAME		"qca,pwr"
/* SoC optional power regulator prefix for DTS */
#define QCA1530_OF_PWR_OPT_REG_NAME	"qca,pwr2"
/* SoC power regulator pin for DTS */
#define QCA1530_OF_PWR_GPIO_NAME	"qca,pwr-gpio"
/* Reset power regulator prefix for DTS */
@@ -49,6 +54,14 @@
/* xLNA pin name for DTS */
#define QCA1530_OF_XLNA_GPIO_NAME	"qca,xlna-gpio"

#define QCA1530_ALL_FLG		0x0f
#define QCA1530_POWER_FLG	0x01
#define QCA1530_XLNA_FLG	0x02
#define QCA1530_CLK_FLG		0x04
#define QCA1530_RESET_FLG	0x08

#define SYSFS_NODE_NAME			"qca1530"

/**
 * struct qca1530_static - keeps all driver instance variables
 * @pdev:          Platform device data
@@ -72,9 +85,11 @@ struct qca1530_static {
	int			rtc_clk_gpio;
	struct clk		*tcxo_clk;
	struct regulator	*pwr_reg;
	struct regulator	*pwr_opt_reg;
	int			pwr_gpio;
	struct regulator	*xlna_reg;
	int			xlna_gpio;
	int			chip_state;
};

/*
@@ -85,6 +100,7 @@ static struct qca1530_static qca1530_data = {
	.rtc_clk_gpio = -1,
	.pwr_gpio = -1,
	.xlna_gpio = -1,
	.chip_state = 0,
};

/**
@@ -143,7 +159,7 @@ static int qca1530_clk_prepare(struct clk *clk, int mode)
	else
		clk_disable_unprepare(clk);

	pr_debug("Configured clk (%p): mode=%d ret=%d", clk, mode, ret);
	pr_debug("Configured clock (%p): mode=%d ret=%d", clk, mode, ret);

	return ret;
}
@@ -157,8 +173,7 @@ static int qca1530_clk_prepare(struct clk *clk, int mode)
static void qca1530_clk_set_gpio(int mode)
{
	gpio_set_value(qca1530_data.rtc_clk_gpio, mode ? 1 : 0);

	pr_debug("Configured clk (GPIO): mode=%d", mode);
	pr_debug("Set clk GPIO (%d): mode=%d", qca1530_data.rtc_clk_gpio, mode);
}

/**
@@ -175,51 +190,57 @@ static int qca1530_clk_set(int mode)
{
	int ret = 0;

	if (qca1530_data.rtc_clk_gpio < 0 && !qca1530_data.rtc_clk
		&& !qca1530_data.tcxo_clk) {
	if (qca1530_data.rtc_clk_gpio < 0 &&
		!qca1530_data.rtc_clk &&
		!qca1530_data.tcxo_clk) {
		ret = -ENOSYS;
	} else if (mode) {
	} else {
		if (qca1530_data.rtc_clk_gpio >= 0)
			qca1530_clk_set_gpio(1);
			qca1530_clk_set_gpio(mode);
		if (qca1530_data.rtc_clk)
			ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 1);
			ret = qca1530_clk_prepare(qca1530_data.rtc_clk, mode);
		if (!ret && qca1530_data.tcxo_clk)
			ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 1);
	} else {
		if (qca1530_data.tcxo_clk)
			ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 0);
		if (!ret && qca1530_data.rtc_clk)
			ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 0);
		if (!ret && qca1530_data.rtc_clk_gpio >= 0)
			qca1530_clk_set_gpio(0);
			ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, mode);
	}

	pr_debug("Configured clk: mode=%d ret=%d", mode, ret);

	return ret;
}

/**
 * qca1530_clk_release_clocks() - release clocks
 * qca1530_clk_release_clock() - release clocks
 * @pdev: platform device data
 * @clk_name: clock name
 * @clk: pointer to clock handle pointer
 *
 * Function releases initialized clocks and sets clock handle
 * references to NULL.
 * pointer to NULL.
 */
static void qca1530_clk_release_clocks(struct platform_device *pdev)
static void qca1530_clk_release_clock(
	struct platform_device *pdev,
	const char *clk_name,
	struct clk **clk)
{
	if (qca1530_data.tcxo_clk) {
		pr_debug("Unregistering CLK: device=%s name=%s",
			dev_name(&pdev->dev), QCA1530_TCXO_CLK_ID);
		devm_clk_put(&pdev->dev, qca1530_data.tcxo_clk);
		qca1530_data.tcxo_clk = NULL;
	if (*clk) {
		pr_debug("Unregistering CLK: name=%s", clk_name);
		devm_clk_put(&pdev->dev, *clk);
		*clk = NULL;
	}
	if (qca1530_data.rtc_clk) {
		pr_debug("Unregistering CLK: device=%s name=%s",
			dev_name(&pdev->dev), QCA1530_RTC_CLK_ID);
		devm_clk_put(&pdev->dev, qca1530_data.rtc_clk);
		qca1530_data.rtc_clk = NULL;
}

/**
 * qca1530_clk_release_clocks() - release clocks
 * @pdev: platform device data
 *
 * Function releases initialized clocks and sets clock handle
 * pointers to NULL.
 */
static void qca1530_clk_release_clocks(struct platform_device *pdev)
{
	qca1530_clk_release_clock(pdev, QCA1530_TCXO_CLK_ID,
		&qca1530_data.tcxo_clk);
	qca1530_clk_release_clock(pdev, QCA1530_RTC_CLK_ID,
		&qca1530_data.rtc_clk);
}

/**
@@ -237,13 +258,15 @@ static int qca1530_clk_init_gpio(struct platform_device *pdev)
	ret = of_get_named_gpio(pdev->dev.of_node, QCA1530_OF_CLK_GPIO_NAME, 0);
	if (ret == -ENOENT) {
		qca1530_data.rtc_clk_gpio = ret;
		pr_debug("%s GPIO is not defined", QCA1530_OF_CLK_GPIO_NAME);
		ret = 0;
		pr_debug("GPIO is not defined");
	} else if (ret < 0) {
		pr_err("GPIO error: %d", ret);
		pr_err("Error getting GPIO %s: %d", QCA1530_OF_CLK_GPIO_NAME,
			ret);
	} else {
		qca1530_data.rtc_clk_gpio = ret;
		pr_debug("GPIO registered: gpio=%d", ret);
		pr_debug("GPIO %s registered: gpio=%d",
			QCA1530_OF_CLK_GPIO_NAME, ret);
		ret = 0;
	}
	return ret;
@@ -262,7 +285,7 @@ static int qca1530_clk_init(struct platform_device *pdev)
{
	int ret;

	pr_debug("Clock initializing");
	pr_debug("Initializing clock");

	qca1530_data.rtc_clk = devm_clk_get(&pdev->dev, QCA1530_RTC_CLK_ID);

@@ -272,12 +295,15 @@ static int qca1530_clk_init(struct platform_device *pdev)
		if (ret == -ENOENT) {
			pr_debug("No RTC clock controller");
		} else {
			pr_err("error: device=%s clock=%s ret=%d",
			pr_err("Clock init error: device=%s clock=%s ret=%d",
				dev_name(&pdev->dev), QCA1530_RTC_CLK_ID,
				ret);
			goto err_0;
		}
	}
	} else
		pr_debug("Ref to clock %s obtained: %p",
				QCA1530_RTC_CLK_ID, qca1530_data.rtc_clk);

	qca1530_data.tcxo_clk = devm_clk_get(&pdev->dev, QCA1530_TCXO_CLK_ID);
	if (IS_ERR(qca1530_data.tcxo_clk)) {
		ret = PTR_ERR(qca1530_data.tcxo_clk);
@@ -285,23 +311,26 @@ static int qca1530_clk_init(struct platform_device *pdev)
		if (ret == -ENOENT) {
			pr_debug("No TCXO clock controller");
		} else {
			pr_err("error: device=%s clock=%s ret=%d",
			pr_err("Clock init error: device=%s clock=%s ret=%d",
				dev_name(&pdev->dev), QCA1530_TCXO_CLK_ID,
				ret);
			goto err_1;
		}
	}
	} else
		pr_debug("Ref to clock %s obtained: %p",
				QCA1530_TCXO_CLK_ID, qca1530_data.tcxo_clk);

	ret = qca1530_clk_init_gpio(pdev);
	if (ret)
		goto err_1;

	ret = qca1530_clk_set(1);
	if (ret < 0) {
		pr_err("error: ret=%d", ret);
		pr_err("Clock set error: ret=%d", ret);
		goto err_2;
	}

	pr_debug("init done: GPIO=%s RTC=%s TCXO=%s",
	pr_debug("Clock init done: GPIO=%s RTC=%s TCXO=%s",
		qca1530_data.rtc_clk_gpio >= 0 ? "ok" : "unused",
		qca1530_data.rtc_clk ? "ok" : "unused",
		qca1530_data.tcxo_clk ? "ok" : "unused");
@@ -379,7 +408,7 @@ static int qca1530_pwr_set_regulator(struct regulator *reg, int mode)
		}
	}

	pr_debug("Regulator result: regulator=%p mode=%d ret=%d", reg, mode,
	pr_debug("Regulator set result: regulator=%p mode=%d ret=%d", reg, mode,
		ret);

	return ret;
@@ -404,6 +433,8 @@ static int qca1530_pwr_init_gpio(struct platform_device *pdev)
	} else if (ret < 0) {
		pr_err("Power control GPIO error: %d", ret);
	} else {
		pr_debug("Ref to gpio %s obtained: %d",
				QCA1530_OF_PWR_GPIO_NAME, ret);
		qca1530_data.pwr_gpio = ret;
		ret = 0;
	}
@@ -438,6 +469,7 @@ static int qca1530_pwr_init_regulator(
		} else
			pr_err("Failed to get regulator, ret=%d", ret);
	} else {
		pr_debug("Ref to regulator %s obtained: %p", name, pwr);
		*ppwr = pwr;
		ret = 0;
	}
@@ -455,20 +487,19 @@ static int qca1530_pwr_init_regulator(
static int qca1530_pwr_set(int mode)
{
	int ret = 0;
	if (!qca1530_data.pwr_reg && qca1530_data.pwr_gpio < 0) {
	if (!qca1530_data.pwr_reg &&
		!qca1530_data.pwr_opt_reg &&
		qca1530_data.pwr_gpio < 0) {
		ret = -ENOSYS;
	} else if (mode) {
	} else {
		if (qca1530_data.pwr_reg)
			ret = qca1530_pwr_set_regulator(
				qca1530_data.pwr_reg, mode);
		if (!ret && qca1530_data.pwr_opt_reg)
			ret = qca1530_pwr_set_regulator(
				qca1530_data.pwr_opt_reg, mode);
		if (!ret && qca1530_data.pwr_gpio >= 0)
			qca1530_pwr_set_gpio(mode);
	} else {
		if (qca1530_data.pwr_gpio >= 0)
			qca1530_pwr_set_gpio(mode);
		if (!ret && qca1530_data.pwr_reg)
			ret = qca1530_pwr_set_regulator(
				qca1530_data.pwr_reg, mode);
	}
	return ret;
}
@@ -492,15 +523,22 @@ static int qca1530_pwr_init(struct platform_device *pdev)
	if (ret)
		goto err_0;

	ret = qca1530_pwr_init_gpio(pdev);
	ret = qca1530_pwr_init_regulator(
		pdev, QCA1530_OF_PWR_OPT_REG_NAME, &qca1530_data.pwr_opt_reg);
	if (ret)
		goto err_1;

	if (qca1530_data.pwr_reg || qca1530_data.pwr_gpio >= 0) {
	ret = qca1530_pwr_init_gpio(pdev);
	if (ret)
		goto err_2;

	if (qca1530_data.pwr_reg ||
		qca1530_data.pwr_gpio >= 0 ||
		qca1530_data.pwr_opt_reg) {
		ret = qca1530_pwr_set(1);
		if (ret) {
			pr_err("Failed to enable power, rc=%d", ret);
			goto err_2;
			goto err_3;
		}
		pr_debug("Configured: reg=%p gpio=%d",
			qca1530_data.pwr_reg,
@@ -510,8 +548,10 @@ static int qca1530_pwr_init(struct platform_device *pdev)
	}

	return ret;
err_2:
err_3:
	qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio);
err_2:
	qca1530_deinit_regulator(&qca1530_data.pwr_opt_reg);
err_1:
	qca1530_deinit_regulator(&qca1530_data.pwr_reg);
err_0:
@@ -528,6 +568,7 @@ static void qca1530_pwr_deinit(struct platform_device *pdev)
{
	qca1530_pwr_set(0);
	qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio);
	qca1530_deinit_regulator(&qca1530_data.pwr_opt_reg);
	qca1530_deinit_regulator(&qca1530_data.pwr_reg);
}

@@ -544,7 +585,7 @@ static int qca1530_reset_init(struct platform_device *pdev)
{
	int ret;

	pr_debug("reset control: initializing");
	pr_debug("Initializing reset control");

	ret = of_get_named_gpio(pdev->dev.of_node, QCA1530_OF_RESET_GPIO_NAME,
				0);
@@ -552,6 +593,8 @@ static int qca1530_reset_init(struct platform_device *pdev)
		pr_err("failed to get gpio from config: %d", ret);
		goto err_gpio_get;
	}
	pr_debug("Ref to gpio %s obtained: %d", QCA1530_OF_RESET_GPIO_NAME,
		ret);

	qca1530_data.reset_gpio = ret;
	ret = devm_gpio_request(&pdev->dev, qca1530_data.reset_gpio,
@@ -606,7 +649,7 @@ err_gpio_get:
 */
static void qca1530_reset_deinit(struct platform_device *pdev)
{
	pr_debug("reset control: releasing");
	pr_debug("Releasing reset control");
	qca1530_deinit_gpio(pdev, &qca1530_data.reset_gpio);
	if (qca1530_data.reset_reg) {
		qca1530_pwr_set_regulator(qca1530_data.reset_reg, 0);
@@ -635,6 +678,8 @@ static int qca1530_xlna_init_gpio(struct platform_device *pdev)
	} else if (ret < 0) {
		pr_err("xLNA control: GPIO error: %d", ret);
	} else {
		pr_debug("Ref to gpio %s obtained: %d",
			QCA1530_OF_XLNA_GPIO_NAME, ret);
		qca1530_data.xlna_gpio = ret;
		ret = 0;
	}
@@ -726,7 +771,7 @@ static int qca1530_xlna_init(struct platform_device *pdev)
	int ret = 0;
	u32 tmp[2];

	pr_debug("xLNA control: initializing");
	pr_debug("Initializing xLNA control");

	ret = qca1530_pwr_init_regulator(
		pdev, QCA1530_OF_XLNA_REG_NAME, &qca1530_data.xlna_reg);
@@ -800,8 +845,9 @@ static void qca1530_xlna_deinit(struct platform_device *pdev)
}

/**
 * qca1530_probe() - performs driver initialization
 * qca1530_set_chip_state() - performs driver initialization
 * @pdev: platform device data
 * @on: target state, 1 - on, 0 - off
 *
 * The driver probing includes initialization of the subsystems in the
 * following order:
@@ -810,50 +856,202 @@ static void qca1530_xlna_deinit(struct platform_device *pdev)
 *   - xLNA control
 *   - SoC power control
 */
static int qca1530_probe(struct platform_device *pdev)
static int qca1530_set_chip_state(struct platform_device *pdev,
	const int on_mask)
{
	int ret;
	int on_req;

	ret = 0;
	on_req = on_mask;
	pr_debug("on_mask=%d", on_mask);

	pr_debug("Switching on");

	if (!(qca1530_data.chip_state & QCA1530_RESET_FLG) &&
		(on_req & QCA1530_RESET_FLG)) {
		ret = qca1530_reset_init(pdev);
		if (ret < 0) {
			pr_err("failed to init reset: %d", ret);
		goto err_reset_init;
			on_req = 0;
			goto switching_off;
		} else {
			qca1530_data.chip_state |= QCA1530_RESET_FLG;
		}
	}

	if (!(qca1530_data.chip_state & QCA1530_CLK_FLG) &&
		(on_req & QCA1530_CLK_FLG)) {
		ret = qca1530_clk_init(pdev);
		if (ret) {
			pr_err("failed to initialize clock: %d", ret);
		goto err_clk_init;
			on_req = 0;
			goto switching_off;
		} else {
			qca1530_data.chip_state |= QCA1530_CLK_FLG;
		}
	}

	if (!(qca1530_data.chip_state & QCA1530_XLNA_FLG) &&
		(on_req & QCA1530_XLNA_FLG)) {
		ret = qca1530_xlna_init(pdev);
		if (ret) {
			pr_err("failed to initialize xLNA: %d", ret);
		goto err_xlna_init;
			on_req = 0;
			goto switching_off;
		} else {
			qca1530_data.chip_state |= QCA1530_XLNA_FLG;
		}
	}

	if (!(qca1530_data.chip_state & QCA1530_POWER_FLG) &&
		(on_req & QCA1530_POWER_FLG)) {
		ret = qca1530_pwr_init(pdev);
		if (ret < 0) {
			pr_err("failed to init power: %d", ret);
		goto err_pwr_init;
			on_req = 0;
			goto switching_off;
		} else {
			qca1530_data.chip_state |= QCA1530_POWER_FLG;
		}
	}

	qca1530_data.pdev = pdev;
	pr_debug("Probe OK");
	return ret;
	pr_debug("Chip on section over, qca1530_data.chip_state=%d",
		qca1530_data.chip_state);

err_pwr_init:
switching_off:
	pr_debug("Switching off");

	if ((qca1530_data.chip_state & QCA1530_POWER_FLG) &&
		!(on_req & QCA1530_POWER_FLG)) {
			qca1530_pwr_deinit(pdev);
			qca1530_data.chip_state &= ~QCA1530_POWER_FLG;
	}

	if ((qca1530_data.chip_state & QCA1530_XLNA_FLG) &&
		!(on_req & QCA1530_XLNA_FLG)) {
			qca1530_xlna_deinit(pdev);
err_xlna_init:
			qca1530_data.chip_state &= ~QCA1530_XLNA_FLG;
	}

	if ((qca1530_data.chip_state & QCA1530_CLK_FLG) &&
		!(on_req & QCA1530_CLK_FLG)) {
			qca1530_clk_deinit(pdev);
err_clk_init:
			qca1530_data.chip_state &= ~QCA1530_CLK_FLG;
	}

	if ((qca1530_data.chip_state & QCA1530_RESET_FLG) &&
		!(on_req & QCA1530_RESET_FLG)) {
			qca1530_reset_deinit(pdev);
err_reset_init:
			qca1530_data.chip_state &= ~QCA1530_RESET_FLG;
	}

	pr_debug("Chip off section over, qca1530_data.chip_state=%d",
		qca1530_data.chip_state);

	pr_debug("Probe ret=%d", ret);
	return ret;
}


static ssize_t chip_state_show(struct kobject *kobj,
	struct kobj_attribute *attr,
	char *buf)
{
	return snprintf(buf, 16, "%d\n", qca1530_data.chip_state);
}

static ssize_t chip_state_store(struct kobject *kobj,
	struct kobj_attribute *attr,
	const char *buf, size_t count)
{
	int retval;
	int new_state;

	sscanf(buf, "%du", &new_state);
	pr_debug("new_state=%d", new_state);
	if (count &&
		((new_state <= QCA1530_ALL_FLG) && (new_state >= 0))) {
		pr_debug("qca1530_data.chip_state=%d", qca1530_data.chip_state);
		retval = qca1530_set_chip_state(qca1530_data.pdev, new_state);
		pr_debug("qca1530_set_chip_state() returned %d", retval);
		pr_debug("qca1530_data.chip_state=%d", qca1530_data.chip_state);
	}

	return count;
}

static struct kobj_attribute chip_state_attribute =
	__ATTR(chip_state, 0600, chip_state_show, chip_state_store);

static struct attribute *attrs[] = {
	&chip_state_attribute.attr,
	NULL,
};

static struct attribute_group attr_group = {
	.attrs = attrs,
};

static struct kobject *qca1530_kobject;

static int qca1530_create_sysfs_node(void)
{
	int retval;

	pr_debug("Creating sysfs node");

	qca1530_kobject = kobject_create_and_add(SYSFS_NODE_NAME, kernel_kobj);
	pr_debug("qca1530_kobject=%p", qca1530_kobject);
	if (!qca1530_kobject)
		return -ENOMEM;

	retval = sysfs_create_group(qca1530_kobject, &attr_group);
	pr_debug("sysfs_create_group() returned %d", retval);
	if (retval)
		kobject_put(qca1530_kobject);

	return retval;
}

static void qca1530_remove_sysfs_node(void)
{
	pr_debug("Removing sysfs node");
	if (qca1530_kobject) {
		sysfs_remove_group(qca1530_kobject, &attr_group);
		kobject_put(qca1530_kobject);
		qca1530_kobject = NULL;
	}
}

/**
 * qca1530_probe() - performs driver initialization
 * @pdev: platform device data
 *
 * Function tried to turn on all required signals.
 * Refer to qca1530_set_chip_state() documentation for details.
 */
static int qca1530_probe(struct platform_device *pdev)
{
	int retval;

	pr_debug("Probing to install");

	retval = qca1530_set_chip_state(pdev, QCA1530_ALL_FLG);
	if (!retval) {
		qca1530_data.pdev = pdev;
		retval = qca1530_create_sysfs_node();
		if (retval) {
			qca1530_set_chip_state(pdev, 0);
			qca1530_data.pdev = NULL;
			pr_debug("qca1530_create_sysfs_node() returned %d",
			retval);
		}
	} else
		pr_debug("qca1530_set_chip_state() returned %d", retval);

	return retval;
}

/**
 * qca1530_remove() - releases all resources
 * @pdev: platform device data
@@ -866,12 +1064,11 @@ err_reset_init:
 */
static int qca1530_remove(struct platform_device *pdev)
{
	qca1530_pwr_deinit(pdev);
	qca1530_xlna_deinit(pdev);
	qca1530_clk_deinit(pdev);
	qca1530_reset_deinit(pdev);
	int retval;
	qca1530_remove_sysfs_node();
	retval = qca1530_set_chip_state(pdev, 0);
	qca1530_data.pdev = NULL;
	return 0;
	return retval;
}

/*