Message ID | 20190812123253.4734-3-jbrunet@baylibre.com (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Neil Armstrong |
Headers | show |
Series | clk: meson: axg-audio: add reset support | expand |
On 12/08/2019 14:32, Jerome Brunet wrote: > On the g12a, the register space dedicated to the audio clock also > provides some resets. Let the clock controller register a reset > provider as well for this SoC family. > > the axg SoC family does not appear to provide this feature. > > Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> > --- > drivers/clk/meson/axg-audio.c | 107 +++++++++++++++++++++++++++++++++- > drivers/clk/meson/axg-audio.h | 1 + > 2 files changed, 106 insertions(+), 2 deletions(-) > > diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c > index 741df7e955ca..6be9df1efce5 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; > @@ -984,8 +1066,27 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) > } > } > > - 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 +1095,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[] = { > diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h > index 5d972d55d6c7..c00e28b2e1a9 100644 > --- a/drivers/clk/meson/axg-audio.h > +++ b/drivers/clk/meson/axg-audio.h > @@ -22,6 +22,7 @@ > #define AUDIO_MCLK_F_CTRL 0x018 > #define AUDIO_MST_PAD_CTRL0 0x01c > #define AUDIO_MST_PAD_CTRL1 0x020 > +#define AUDIO_SW_RESET 0x024 > #define AUDIO_MST_A_SCLK_CTRL0 0x040 > #define AUDIO_MST_A_SCLK_CTRL1 0x044 > #define AUDIO_MST_B_SCLK_CTRL0 0x048 > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 741df7e955ca..6be9df1efce5 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; @@ -984,8 +1066,27 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) } } - 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 +1095,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[] = { diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h index 5d972d55d6c7..c00e28b2e1a9 100644 --- a/drivers/clk/meson/axg-audio.h +++ b/drivers/clk/meson/axg-audio.h @@ -22,6 +22,7 @@ #define AUDIO_MCLK_F_CTRL 0x018 #define AUDIO_MST_PAD_CTRL0 0x01c #define AUDIO_MST_PAD_CTRL1 0x020 +#define AUDIO_SW_RESET 0x024 #define AUDIO_MST_A_SCLK_CTRL0 0x040 #define AUDIO_MST_A_SCLK_CTRL1 0x044 #define AUDIO_MST_B_SCLK_CTRL0 0x048
On the g12a, the register space dedicated to the audio clock also provides some resets. Let the clock controller register a reset provider as well for this SoC family. the axg SoC family does not appear to provide this feature. Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> --- drivers/clk/meson/axg-audio.c | 107 +++++++++++++++++++++++++++++++++- drivers/clk/meson/axg-audio.h | 1 + 2 files changed, 106 insertions(+), 2 deletions(-)