diff options
Diffstat (limited to 'drivers/clk/meson/axg-audio.c')
-rw-r--r-- | drivers/clk/meson/axg-audio.c | 114 |
1 files changed, 110 insertions, 4 deletions
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 741df7e955ca..18b23cdf679c 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -12,6 +12,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reset.h> +#include <linux/reset-controller.h> #include <linux/slab.h> #include "axg-audio.h" @@ -918,6 +919,84 @@ static int devm_clk_get_enable(struct device *dev, char *id) return 0; } +struct axg_audio_reset_data { + struct reset_controller_dev rstc; + struct regmap *map; + unsigned int offset; +}; + +static void axg_audio_reset_reg_and_bit(struct axg_audio_reset_data *rst, + unsigned long id, + unsigned int *reg, + unsigned int *bit) +{ + unsigned int stride = regmap_get_reg_stride(rst->map); + + *reg = (id / (stride * BITS_PER_BYTE)) * stride; + *reg += rst->offset; + *bit = id % (stride * BITS_PER_BYTE); +} + +static int axg_audio_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct axg_audio_reset_data *rst = + container_of(rcdev, struct axg_audio_reset_data, rstc); + unsigned int offset, bit; + + axg_audio_reset_reg_and_bit(rst, id, &offset, &bit); + + regmap_update_bits(rst->map, offset, BIT(bit), + assert ? BIT(bit) : 0); + + return 0; +} + +static int axg_audio_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct axg_audio_reset_data *rst = + container_of(rcdev, struct axg_audio_reset_data, rstc); + unsigned int val, offset, bit; + + axg_audio_reset_reg_and_bit(rst, id, &offset, &bit); + + regmap_read(rst->map, offset, &val); + + return !!(val & BIT(bit)); +} + +static int axg_audio_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return axg_audio_reset_update(rcdev, id, true); +} + +static int axg_audio_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return axg_audio_reset_update(rcdev, id, false); +} + +static int axg_audio_reset_toggle(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = axg_audio_reset_assert(rcdev, id); + if (ret) + return ret; + + return axg_audio_reset_deassert(rcdev, id); +} + +static const struct reset_control_ops axg_audio_rstc_ops = { + .assert = axg_audio_reset_assert, + .deassert = axg_audio_reset_deassert, + .reset = axg_audio_reset_toggle, + .status = axg_audio_reset_status, +}; + static const struct regmap_config axg_audio_regmap_cfg = { .reg_bits = 32, .val_bits = 32, @@ -927,12 +1006,15 @@ static const struct regmap_config axg_audio_regmap_cfg = { struct audioclk_data { struct clk_hw_onecell_data *hw_onecell_data; + unsigned int reset_offset; + unsigned int reset_num; }; static int axg_audio_clkc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct audioclk_data *data; + struct axg_audio_reset_data *rst; struct regmap *map; struct resource *res; void __iomem *regs; @@ -971,21 +1053,43 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) /* Take care to skip the registered input clocks */ for (i = AUD_CLKID_DDR_ARB; i < data->hw_onecell_data->num; i++) { + const char *name; + hw = data->hw_onecell_data->hws[i]; /* array might be sparse */ if (!hw) continue; + name = hw->init->name; + ret = devm_clk_hw_register(dev, hw); if (ret) { - dev_err(dev, "failed to register clock %s\n", - hw->init->name); + dev_err(dev, "failed to register clock %s\n", name); return ret; } } - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - data->hw_onecell_data); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + data->hw_onecell_data); + if (ret) + return ret; + + /* Stop here if there is no reset */ + if (!data->reset_num) + return 0; + + rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL); + if (!rst) + return -ENOMEM; + + rst->map = map; + rst->offset = data->reset_offset; + rst->rstc.nr_resets = data->reset_num; + rst->rstc.ops = &axg_audio_rstc_ops; + rst->rstc.of_node = dev->of_node; + rst->rstc.owner = THIS_MODULE; + + return devm_reset_controller_register(dev, &rst->rstc); } static const struct audioclk_data axg_audioclk_data = { @@ -994,6 +1098,8 @@ static const struct audioclk_data axg_audioclk_data = { static const struct audioclk_data g12a_audioclk_data = { .hw_onecell_data = &g12a_audio_hw_onecell_data, + .reset_offset = AUDIO_SW_RESET, + .reset_num = 26, }; static const struct of_device_id clkc_match_table[] = { |