diff options
Diffstat (limited to 'drivers/remoteproc/qcom_wcnss_iris.c')
-rw-r--r-- | drivers/remoteproc/qcom_wcnss_iris.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c new file mode 100644 index 000000000..09720dddd --- /dev/null +++ b/drivers/remoteproc/qcom_wcnss_iris.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm Wireless Connectivity Subsystem Iris driver + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#include "qcom_wcnss.h" + +struct qcom_iris { + struct device dev; + + struct clk *xo_clk; + + struct regulator_bulk_data *vregs; + size_t num_vregs; +}; + +struct iris_data { + const struct wcnss_vreg_info *vregs; + size_t num_vregs; + + bool use_48mhz_xo; +}; + +static const struct iris_data wcn3620_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 3300000, 3300000, 515000 }, + { "vdddig", 1800000, 1800000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = false, +}; + +static const struct iris_data wcn3660_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 2900000, 3000000, 515000 }, + { "vdddig", 1200000, 1225000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = true, +}; + +static const struct iris_data wcn3680_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 3300000, 3300000, 515000 }, + { "vdddig", 1800000, 1800000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = true, +}; + +int qcom_iris_enable(struct qcom_iris *iris) +{ + int ret; + + ret = regulator_bulk_enable(iris->num_vregs, iris->vregs); + if (ret) + return ret; + + ret = clk_prepare_enable(iris->xo_clk); + if (ret) { + dev_err(&iris->dev, "failed to enable xo clk\n"); + goto disable_regulators; + } + + return 0; + +disable_regulators: + regulator_bulk_disable(iris->num_vregs, iris->vregs); + + return ret; +} + +void qcom_iris_disable(struct qcom_iris *iris) +{ + clk_disable_unprepare(iris->xo_clk); + regulator_bulk_disable(iris->num_vregs, iris->vregs); +} + +static const struct of_device_id iris_of_match[] = { + { .compatible = "qcom,wcn3620", .data = &wcn3620_data }, + { .compatible = "qcom,wcn3660", .data = &wcn3660_data }, + { .compatible = "qcom,wcn3660b", .data = &wcn3680_data }, + { .compatible = "qcom,wcn3680", .data = &wcn3680_data }, + {} +}; + +static void qcom_iris_release(struct device *dev) +{ + struct qcom_iris *iris = container_of(dev, struct qcom_iris, dev); + + of_node_put(iris->dev.of_node); + kfree(iris); +} + +struct qcom_iris *qcom_iris_probe(struct device *parent, bool *use_48mhz_xo) +{ + const struct of_device_id *match; + const struct iris_data *data; + struct device_node *of_node; + struct qcom_iris *iris; + int ret; + int i; + + of_node = of_get_child_by_name(parent->of_node, "iris"); + if (!of_node) { + dev_err(parent, "No child node \"iris\" found\n"); + return ERR_PTR(-EINVAL); + } + + iris = kzalloc(sizeof(*iris), GFP_KERNEL); + if (!iris) { + of_node_put(of_node); + return ERR_PTR(-ENOMEM); + } + + device_initialize(&iris->dev); + iris->dev.parent = parent; + iris->dev.release = qcom_iris_release; + iris->dev.of_node = of_node; + + dev_set_name(&iris->dev, "%s.iris", dev_name(parent)); + + ret = device_add(&iris->dev); + if (ret) { + put_device(&iris->dev); + return ERR_PTR(ret); + } + + match = of_match_device(iris_of_match, &iris->dev); + if (!match) { + dev_err(&iris->dev, "no matching compatible for iris\n"); + ret = -EINVAL; + goto err_device_del; + } + + data = match->data; + + iris->xo_clk = devm_clk_get(&iris->dev, "xo"); + if (IS_ERR(iris->xo_clk)) { + ret = PTR_ERR(iris->xo_clk); + if (ret != -EPROBE_DEFER) + dev_err(&iris->dev, "failed to acquire xo clk\n"); + goto err_device_del; + } + + iris->num_vregs = data->num_vregs; + iris->vregs = devm_kcalloc(&iris->dev, + iris->num_vregs, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!iris->vregs) { + ret = -ENOMEM; + goto err_device_del; + } + + for (i = 0; i < iris->num_vregs; i++) + iris->vregs[i].supply = data->vregs[i].name; + + ret = devm_regulator_bulk_get(&iris->dev, iris->num_vregs, iris->vregs); + if (ret) { + dev_err(&iris->dev, "failed to get regulators\n"); + goto err_device_del; + } + + for (i = 0; i < iris->num_vregs; i++) { + if (data->vregs[i].max_voltage) + regulator_set_voltage(iris->vregs[i].consumer, + data->vregs[i].min_voltage, + data->vregs[i].max_voltage); + + if (data->vregs[i].load_uA) + regulator_set_load(iris->vregs[i].consumer, + data->vregs[i].load_uA); + } + + *use_48mhz_xo = data->use_48mhz_xo; + + return iris; + +err_device_del: + device_del(&iris->dev); + + return ERR_PTR(ret); +} + +void qcom_iris_remove(struct qcom_iris *iris) +{ + device_del(&iris->dev); +} |