diff options
| -rw-r--r-- | Documentation/devicetree/bindings/platform/msm/qca1530.txt | 1 | ||||
| -rw-r--r-- | drivers/platform/msm/qca1530.c | 379 |
2 files changed, 289 insertions, 91 deletions
diff --git a/Documentation/devicetree/bindings/platform/msm/qca1530.txt b/Documentation/devicetree/bindings/platform/msm/qca1530.txt index 2b6937d0498..4a1c90bdc28 100644 --- a/Documentation/devicetree/bindings/platform/msm/qca1530.txt +++ b/Documentation/devicetree/bindings/platform/msm/qca1530.txt @@ -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 diff --git a/drivers/platform/msm/qca1530.c b/drivers/platform/msm/qca1530.c index 60e4c9a3eee..125befc0daa 100644 --- a/drivers/platform/msm/qca1530.c +++ b/drivers/platform/msm/qca1530.c @@ -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,19 +85,22 @@ 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; }; /* * qca1530_data - driver instance data */ -static struct qca1530_static qca1530_data = { +static struct qca1530_static qca1530_data = { .reset_gpio = -1, .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_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 + * pointer to NULL. + */ +static void qca1530_clk_release_clock( + struct platform_device *pdev, + const char *clk_name, + struct clk **clk) +{ + if (*clk) { + pr_debug("Unregistering CLK: name=%s", clk_name); + devm_clk_put(&pdev->dev, *clk); + *clk = NULL; + } +} + +/** * qca1530_clk_release_clocks() - release clocks * @pdev: platform device data * * Function releases initialized clocks and sets clock handle - * references to NULL. + * pointers to NULL. */ static void qca1530_clk_release_clocks(struct platform_device *pdev) { - 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 (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_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 = qca1530_reset_init(pdev); - if (ret < 0) { - pr_err("failed to init reset: %d", ret); - goto err_reset_init; + 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); + 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); + on_req = 0; + goto switching_off; + } else { + qca1530_data.chip_state |= QCA1530_CLK_FLG; + } } - ret = qca1530_clk_init(pdev); - if (ret) { - pr_err("failed to initialize clock: %d", ret); - goto err_clk_init; + 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); + on_req = 0; + goto switching_off; + } else { + qca1530_data.chip_state |= QCA1530_XLNA_FLG; + } } - ret = qca1530_xlna_init(pdev); - if (ret) { - pr_err("failed to initialize xLNA: %d", ret); - goto err_xlna_init; + 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); + on_req = 0; + goto switching_off; + } else { + qca1530_data.chip_state |= QCA1530_POWER_FLG; + } } - ret = qca1530_pwr_init(pdev); - if (ret < 0) { - pr_err("failed to init power: %d", ret); - goto err_pwr_init; + pr_debug("Chip on section over, qca1530_data.chip_state=%d", + qca1530_data.chip_state); + +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; } - qca1530_data.pdev = pdev; - pr_debug("Probe OK"); - return ret; + if ((qca1530_data.chip_state & QCA1530_XLNA_FLG) && + !(on_req & QCA1530_XLNA_FLG)) { + qca1530_xlna_deinit(pdev); + qca1530_data.chip_state &= ~QCA1530_XLNA_FLG; + } + + if ((qca1530_data.chip_state & QCA1530_CLK_FLG) && + !(on_req & QCA1530_CLK_FLG)) { + qca1530_clk_deinit(pdev); + qca1530_data.chip_state &= ~QCA1530_CLK_FLG; + } + + if ((qca1530_data.chip_state & QCA1530_RESET_FLG) && + !(on_req & QCA1530_RESET_FLG)) { + qca1530_reset_deinit(pdev); + qca1530_data.chip_state &= ~QCA1530_RESET_FLG; + } -err_pwr_init: - qca1530_xlna_deinit(pdev); -err_xlna_init: - qca1530_clk_deinit(pdev); -err_clk_init: - qca1530_reset_deinit(pdev); -err_reset_init: + 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; } /* |
