diff options
Diffstat (limited to 'drivers/clk/mediatek/reset.c')
-rw-r--r-- | drivers/clk/mediatek/reset.c | 198 |
1 files changed, 145 insertions, 53 deletions
diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index bcec4b89f449..179505549a7c 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -8,55 +8,39 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/reset-controller.h> #include <linux/slab.h> -#include "clk-mtk.h" +#include "reset.h" -struct mtk_reset { - struct regmap *regmap; - int regofs; - struct reset_controller_dev rcdev; -}; - -static int mtk_reset_assert_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) +static inline struct mtk_clk_rst_data *to_mtk_clk_rst_data(struct reset_controller_dev *rcdev) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - unsigned int reg = data->regofs + ((id / 32) << 4); - - return regmap_write(data->regmap, reg, 1); + return container_of(rcdev, struct mtk_clk_rst_data, rcdev); } -static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) +static int mtk_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool deassert) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - unsigned int reg = data->regofs + ((id / 32) << 4) + 0x4; + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + unsigned int val = deassert ? 0 : ~0; - return regmap_write(data->regmap, reg, 1); + return regmap_update_bits(data->regmap, + data->desc->rst_bank_ofs[id / RST_NR_PER_BANK], + BIT(id % RST_NR_PER_BANK), val); } static int mtk_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - - return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), - BIT(id % 32), ~0); + return mtk_reset_update(rcdev, id, false); } static int mtk_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - - return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), - BIT(id % 32), 0); + return mtk_reset_update(rcdev, id, true); } -static int mtk_reset(struct reset_controller_dev *rcdev, - unsigned long id) +static int mtk_reset(struct reset_controller_dev *rcdev, unsigned long id) { int ret; @@ -67,8 +51,32 @@ static int mtk_reset(struct reset_controller_dev *rcdev, return mtk_reset_deassert(rcdev, id); } +static int mtk_reset_update_set_clr(struct reset_controller_dev *rcdev, + unsigned long id, bool deassert) +{ + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + unsigned int deassert_ofs = deassert ? 0x4 : 0; + + return regmap_write(data->regmap, + data->desc->rst_bank_ofs[id / RST_NR_PER_BANK] + + deassert_ofs, + BIT(id % RST_NR_PER_BANK)); +} + +static int mtk_reset_assert_set_clr(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return mtk_reset_update_set_clr(rcdev, id, false); +} + +static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return mtk_reset_update_set_clr(rcdev, id, true); +} + static int mtk_reset_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { int ret; @@ -90,51 +98,135 @@ static const struct reset_control_ops mtk_reset_ops_set_clr = { .reset = mtk_reset_set_clr, }; -static void mtk_register_reset_controller_common(struct device_node *np, - unsigned int num_regs, int regofs, - const struct reset_control_ops *reset_ops) +static int reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + + if (reset_spec->args[0] >= rcdev->nr_resets || + reset_spec->args[0] >= data->desc->rst_idx_map_nr) + return -EINVAL; + + return data->desc->rst_idx_map[reset_spec->args[0]]; +} + +int mtk_register_reset_controller(struct device_node *np, + const struct mtk_clk_rst_desc *desc) { - struct mtk_reset *data; - int ret; struct regmap *regmap; + const struct reset_control_ops *rcops = NULL; + struct mtk_clk_rst_data *data; + int ret; + + if (!desc) { + pr_err("mtk clock reset desc is NULL\n"); + return -EINVAL; + } + + switch (desc->version) { + case MTK_RST_SIMPLE: + rcops = &mtk_reset_ops; + break; + case MTK_RST_SET_CLR: + rcops = &mtk_reset_ops_set_clr; + break; + default: + pr_err("Unknown reset version %d\n", desc->version); + return -EINVAL; + } regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) { pr_err("Cannot find regmap for %pOF: %pe\n", np, regmap); - return; + return -EINVAL; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) - return; + return -ENOMEM; + data->desc = desc; data->regmap = regmap; - data->regofs = regofs; data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = num_regs * 32; - data->rcdev.ops = reset_ops; + data->rcdev.ops = rcops; data->rcdev.of_node = np; + if (data->desc->rst_idx_map_nr > 0) { + data->rcdev.of_reset_n_cells = 1; + data->rcdev.nr_resets = desc->rst_idx_map_nr; + data->rcdev.of_xlate = reset_xlate; + } else { + data->rcdev.nr_resets = desc->rst_bank_nr * RST_NR_PER_BANK; + } + ret = reset_controller_register(&data->rcdev); if (ret) { pr_err("could not register reset controller: %d\n", ret); kfree(data); - return; + return ret; } -} -void mtk_register_reset_controller(struct device_node *np, - unsigned int num_regs, int regofs) -{ - mtk_register_reset_controller_common(np, num_regs, regofs, - &mtk_reset_ops); + return 0; } -void mtk_register_reset_controller_set_clr(struct device_node *np, - unsigned int num_regs, int regofs) +int mtk_register_reset_controller_with_dev(struct device *dev, + const struct mtk_clk_rst_desc *desc) { - mtk_register_reset_controller_common(np, num_regs, regofs, - &mtk_reset_ops_set_clr); + struct device_node *np = dev->of_node; + struct regmap *regmap; + const struct reset_control_ops *rcops = NULL; + struct mtk_clk_rst_data *data; + int ret; + + if (!desc) { + dev_err(dev, "mtk clock reset desc is NULL\n"); + return -EINVAL; + } + + switch (desc->version) { + case MTK_RST_SIMPLE: + rcops = &mtk_reset_ops; + break; + case MTK_RST_SET_CLR: + rcops = &mtk_reset_ops_set_clr; + break; + default: + dev_err(dev, "Unknown reset version %d\n", desc->version); + return -EINVAL; + } + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) { + dev_err(dev, "Cannot find regmap %pe\n", regmap); + return -EINVAL; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->desc = desc; + data->regmap = regmap; + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = rcops; + data->rcdev.of_node = np; + data->rcdev.dev = dev; + + if (data->desc->rst_idx_map_nr > 0) { + data->rcdev.of_reset_n_cells = 1; + data->rcdev.nr_resets = desc->rst_idx_map_nr; + data->rcdev.of_xlate = reset_xlate; + } else { + data->rcdev.nr_resets = desc->rst_bank_nr * RST_NR_PER_BANK; + } + + ret = devm_reset_controller_register(dev, &data->rcdev); + if (ret) { + dev_err(dev, "could not register reset controller: %d\n", ret); + return ret; + } + + return 0; } MODULE_LICENSE("GPL"); |