This series improves the handling of clock and reset controls of
NVIDA Tegra ALSA drivers. Tegra HDA and AHUB drivers aren't handling
resets properly, which needs to be fixed in order to unblock other patches
related to fixes on the reset controller driver since HDA/AHUB are bound
to fail once reset controller driver will be corrected. In particular ALSA
drivers are relying on implicit de-assertion of resets which is done by the
tegra-clk driver. It's not the business of the clk driver to touch resets
and we need to fix this because it breaks reset/clk programming sequences
of other Tegra drivers.
Dmitry Osipenko (5):
ALSA: hda/tegra: Use clk_bulk helpers
ALSA: hda/tegra: Reset hardware
ASoC: tegra: ahub: Use of_reset_control_array_get_exclusive()
ASoC: tegra: ahub: Use clk_bulk helpers
ASoC: tegra: ahub: Reset hardware properly
sound/pci/hda/hda_tegra.c | 86 +++++++++------------------
sound/soc/tegra/tegra30_ahub.c | 103 ++++++---------------------------
sound/soc/tegra/tegra30_ahub.h | 6 +-
3 files changed, 49 insertions(+), 146 deletions(-)
--
2.29.2
Reset hardware in order to bring it into a predictable state.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/pci/hda/hda_tegra.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
@@ -167,6 +169,12 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;+if(!(chip&&chip->running)){+rc=reset_control_assert(hda->reset);+if(rc)+returnrc;+}+rc=clk_bulk_prepare_enable(hda->nclocks,hda->clocks);if(rc!=0)returnrc;
@@ -176,6 +184,10 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)/* disable controller wake up event*/azx_writew(chip,WAKEEN,azx_readw(chip,WAKEEN)&~STATESTS_INT_MASK);+}else{+rc=reset_control_reset(hda->reset);+if(rc)+returnrc;}return0;
@@ -441,6 +453,12 @@ static int hda_tegra_probe(struct platform_device *pdev)returnerr;}+hda->reset=devm_reset_control_array_get_exclusive(&pdev->dev);+if(IS_ERR(hda->reset)){+err=PTR_ERR(hda->reset);+gotoout_free;+}+hda->clocks[hda->nclocks++].id="hda";hda->clocks[hda->nclocks++].id="hda2hdmi";hda->clocks[hda->nclocks++].id="hda2codec_2x";
@@ -186,7 +155,7 @@ static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev)azx_stop_chip(chip);azx_enter_link_reset(chip);}-hda_tegra_disable_clocks(hda);+clk_bulk_disable_unprepare(hda->nclocks,hda->clocks);return0;}
@@ -198,7 +167,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;-rc=hda_tegra_enable_clocks(hda);+rc=clk_bulk_prepare_enable(hda->nclocks,hda->clocks);if(rc!=0)returnrc;if(chip&&chip->running){
@@ -268,29 +237,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)return0;}-staticinthda_tegra_init_clk(structhda_tegra*hda)-{-structdevice*dev=hda->dev;--hda->hda_clk=devm_clk_get(dev,"hda");-if(IS_ERR(hda->hda_clk)){-dev_err(dev,"failed to get hda clock\n");-returnPTR_ERR(hda->hda_clk);-}-hda->hda2codec_2x_clk=devm_clk_get(dev,"hda2codec_2x");-if(IS_ERR(hda->hda2codec_2x_clk)){-dev_err(dev,"failed to get hda2codec_2x clock\n");-returnPTR_ERR(hda->hda2codec_2x_clk);-}-hda->hda2hdmi_clk=devm_clk_get(dev,"hda2hdmi");-if(IS_ERR(hda->hda2hdmi_clk)){-dev_err(dev,"failed to get hda2hdmi clock\n");-returnPTR_ERR(hda->hda2hdmi_clk);-}--return0;-}-staticinthda_tegra_first_init(structazx*chip,structplatform_device*pdev){structhda_tegra*hda=container_of(chip,structhda_tegra,chip);
@@ -495,7 +441,11 @@ static int hda_tegra_probe(struct platform_device *pdev)returnerr;}-err=hda_tegra_init_clk(hda);+hda->clocks[hda->nclocks++].id="hda";+hda->clocks[hda->nclocks++].id="hda2hdmi";+hda->clocks[hda->nclocks++].id="hda2codec_2x";++err=devm_clk_bulk_get(&pdev->dev,hda->nclocks,hda->clocks);if(err<0)gotoout_free;
Some of resets are erroneously missed in the configlink_mods[], like APBIF
for example. Use of_reset_control_array_get_exclusive() which requests all
the resets. The problem was hidden by the clk driver which implicitly
de-asserts the missing resets.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 66 +++++-----------------------------
sound/soc/tegra/tegra30_ahub.h | 1 -
2 files changed, 9 insertions(+), 58 deletions(-)
@@ -510,7 +472,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev)conststructof_device_id*match;conststructtegra30_ahub_soc_data*soc_data;structreset_control*rst;-inti;structresource*res0;void__iomem*regs_apbif,*regs_ahub;intret=0;
@@ -528,26 +489,17 @@ static int tegra30_ahub_probe(struct platform_device *pdev)*operatecorrectly,alldevicesonthisbusmustbeoutofreset.*Ensurethathere.*/-for(i=0;i<ARRAY_SIZE(configlink_mods);i++){-if(!(configlink_mods[i].mod_list_mask&-soc_data->mod_list_mask))-continue;--rst=reset_control_get_exclusive(&pdev->dev,-configlink_mods[i].rst_name);-if(IS_ERR(rst)){-dev_err(&pdev->dev,"Can't get reset %s\n",-configlink_mods[i].rst_name);-ret=PTR_ERR(rst);-returnret;-}--ret=reset_control_deassert(rst);-reset_control_put(rst);-if(ret)-returnret;+rst=of_reset_control_array_get_exclusive(pdev->dev.of_node);+if(IS_ERR(rst)){+dev_err(&pdev->dev,"Can't get reset: %p\n",rst);+returnPTR_ERR(rst);}+ret=reset_control_deassert(rst);+reset_control_put(rst);+if(ret)+returnret;+ahub=devm_kzalloc(&pdev->dev,sizeof(structtegra30_ahub),GFP_KERNEL);if(!ahub)
Assert hardware reset before clocks are enabled and then de-assert it
after clocks are enabled. This brings hardware into a predictable state
and removes relying on implicit de-assertion of resets which is done by
the clk driver.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 33 ++++++++++++++++-----------------
sound/soc/tegra/tegra30_ahub.h | 1 +
2 files changed, 17 insertions(+), 17 deletions(-)
On Tue, 12 Jan 2021 13:58:29 +0100,
Dmitry Osipenko wrote:
This series improves the handling of clock and reset controls of
NVIDA Tegra ALSA drivers. Tegra HDA and AHUB drivers aren't handling
resets properly, which needs to be fixed in order to unblock other patches
related to fixes on the reset controller driver since HDA/AHUB are bound
to fail once reset controller driver will be corrected. In particular ALSA
drivers are relying on implicit de-assertion of resets which is done by the
tegra-clk driver. It's not the business of the clk driver to touch resets
and we need to fix this because it breaks reset/clk programming sequences
of other Tegra drivers.
Dmitry Osipenko (5):
ALSA: hda/tegra: Use clk_bulk helpers
ALSA: hda/tegra: Reset hardware
ASoC: tegra: ahub: Use of_reset_control_array_get_exclusive()
ASoC: tegra: ahub: Use clk_bulk helpers
ASoC: tegra: ahub: Reset hardware properly
Thierry, Jonathan, Sameer, could you guys check those please?
thanks,
Takashi
From: Ben Dooks <hidden> Date: 2021-01-15 11:24:38
On 12/01/2021 12:58, Dmitry Osipenko wrote:
This series improves the handling of clock and reset controls of
NVIDA Tegra ALSA drivers. Tegra HDA and AHUB drivers aren't handling
resets properly, which needs to be fixed in order to unblock other patches
related to fixes on the reset controller driver since HDA/AHUB are bound
to fail once reset controller driver will be corrected. In particular ALSA
drivers are relying on implicit de-assertion of resets which is done by the
tegra-clk driver. It's not the business of the clk driver to touch resets
and we need to fix this because it breaks reset/clk programming sequences
of other Tegra drivers.
Dmitry Osipenko (5):
ALSA: hda/tegra: Use clk_bulk helpers
ALSA: hda/tegra: Reset hardware
ASoC: tegra: ahub: Use of_reset_control_array_get_exclusive()
ASoC: tegra: ahub: Use clk_bulk helpers
ASoC: tegra: ahub: Reset hardware properly
sound/pci/hda/hda_tegra.c | 86 +++++++++------------------
sound/soc/tegra/tegra30_ahub.c | 103 ++++++---------------------------
sound/soc/tegra/tegra30_ahub.h | 6 +-
3 files changed, 49 insertions(+), 146 deletions(-)
This series improves the handling of clock and reset controls of
NVIDA Tegra ALSA drivers. Tegra HDA and AHUB drivers aren't handling
resets properly, which needs to be fixed in order to unblock other
patches
related to fixes on the reset controller driver since HDA/AHUB are bound
to fail once reset controller driver will be corrected. In particular
ALSA
drivers are relying on implicit de-assertion of resets which is done
by the
tegra-clk driver. It's not the business of the clk driver to touch resets
and we need to fix this because it breaks reset/clk programming sequences
of other Tegra drivers.
Dmitry Osipenko (5):
ALSA: hda/tegra: Use clk_bulk helpers
ALSA: hda/tegra: Reset hardware
ASoC: tegra: ahub: Use of_reset_control_array_get_exclusive()
ASoC: tegra: ahub: Use clk_bulk helpers
ASoC: tegra: ahub: Reset hardware properly
sound/pci/hda/hda_tegra.c | 86 +++++++++------------------
sound/soc/tegra/tegra30_ahub.c | 103 ++++++---------------------------
sound/soc/tegra/tegra30_ahub.h | 6 +-
3 files changed, 49 insertions(+), 146 deletions(-)
I wonder if this will help with the issues we saw when the tegra is
the i2s clock slave.
Probably no, this series shouldn't fix any of the current problems. I
will be surprised if it does.
Assert hardware reset before clocks are enabled and then de-assert it
after clocks are enabled. This brings hardware into a predictable state
and removes relying on implicit de-assertion of resets which is done by
the clk driver.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 33 ++++++++++++++++-----------------
sound/soc/tegra/tegra30_ahub.h | 1 +
2 files changed, 17 insertions(+), 17 deletions(-)
@@ -186,7 +155,7 @@ static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev)azx_stop_chip(chip);azx_enter_link_reset(chip);}-hda_tegra_disable_clocks(hda);+clk_bulk_disable_unprepare(hda->nclocks,hda->clocks);return0;}
@@ -198,7 +167,7 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;-rc=hda_tegra_enable_clocks(hda);+rc=clk_bulk_prepare_enable(hda->nclocks,hda->clocks);if(rc!=0)returnrc;if(chip&&chip->running){
@@ -268,29 +237,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)return0;}-staticinthda_tegra_init_clk(structhda_tegra*hda)-{-structdevice*dev=hda->dev;--hda->hda_clk=devm_clk_get(dev,"hda");-if(IS_ERR(hda->hda_clk)){-dev_err(dev,"failed to get hda clock\n");-returnPTR_ERR(hda->hda_clk);-}-hda->hda2codec_2x_clk=devm_clk_get(dev,"hda2codec_2x");-if(IS_ERR(hda->hda2codec_2x_clk)){-dev_err(dev,"failed to get hda2codec_2x clock\n");-returnPTR_ERR(hda->hda2codec_2x_clk);-}-hda->hda2hdmi_clk=devm_clk_get(dev,"hda2hdmi");-if(IS_ERR(hda->hda2hdmi_clk)){-dev_err(dev,"failed to get hda2hdmi clock\n");-returnPTR_ERR(hda->hda2hdmi_clk);-}--return0;-}-staticinthda_tegra_first_init(structazx*chip,structplatform_device*pdev){structhda_tegra*hda=container_of(chip,structhda_tegra,chip);
@@ -495,7 +441,11 @@ static int hda_tegra_probe(struct platform_device *pdev)returnerr;}-err=hda_tegra_init_clk(hda);+hda->clocks[hda->nclocks++].id="hda";+hda->clocks[hda->nclocks++].id="hda2hdmi";+hda->clocks[hda->nclocks++].id="hda2codec_2x";
Originally the code did this in this order: "hda", "hda2codec_2x" and
"hda2hdmi". I don't expect the exact order to be very relevant, but was
there any particular reason to change it?
In either case, this should be fine:
Acked-by: Thierry Reding <redacted>
On Tue, Jan 12, 2021 at 03:58:31PM +0300, Dmitry Osipenko wrote:
quoted hunk
Reset hardware in order to bring it into a predictable state.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/pci/hda/hda_tegra.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
@@ -167,6 +169,12 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;+if(!(chip&&chip->running)){
Isn't that check for !chip a bit redundant? If that pointer isn't valid,
we're just going to go crash when dereferencing hda later on, so I think
this can simply be:
if (!chip->running)
I guess you took this from the inverse check below, but I think we can
also drop it from there, perhaps in a separate patch.
@@ -176,6 +184,10 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) /* disable controller wake up event*/ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);+ } else {+ rc = reset_control_reset(hda->reset);
The "if (chip)" part definitely doesn't make sense after this anymore
because now if chip == NULL, then we end up in here and dereference an
invalid "hda" pointer.
Also, why reset_control_reset() here? We'll reach this if we ran
reset_control_assert() above, so this should just be
reset_control_deassert() to undo that, right? I suppose it wouldn't hurt
to put throw that standard usleep_range() in there as well that we use
to wait between reset assert and deassert to make sure the clocks have
stabilized and the reset has indeed propagated through the whole IP.
Thierry
On Tue, Jan 12, 2021 at 03:58:32PM +0300, Dmitry Osipenko wrote:
Some of resets are erroneously missed in the configlink_mods[], like APBIF
for example. Use of_reset_control_array_get_exclusive() which requests all
the resets. The problem was hidden by the clk driver which implicitly
de-asserts the missing resets.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 66 +++++-----------------------------
sound/soc/tegra/tegra30_ahub.h | 1 -
2 files changed, 9 insertions(+), 58 deletions(-)
Doing it this way is slightly suboptimal because now we don't actually
have a way of checking that the DT has all the necessary resets listed.
Can we not just make the list complete instead to keep the checks in
place? That should be a much smaller patch, too.
Thierry
On Tue, Jan 12, 2021 at 03:58:34PM +0300, Dmitry Osipenko wrote:
quoted hunk
Assert hardware reset before clocks are enabled and then de-assert it
after clocks are enabled. This brings hardware into a predictable state
and removes relying on implicit de-assertion of resets which is done by
the clk driver.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 33 ++++++++++++++++-----------------
sound/soc/tegra/tegra30_ahub.h | 1 +
2 files changed, 17 insertions(+), 17 deletions(-)
@@ -65,10 +65,20 @@ static int tegra30_ahub_runtime_resume(struct device *dev){intret;+ret=reset_control_assert(ahub->reset);+if(ret)+returnret;+ret=clk_bulk_prepare_enable(ahub->nclocks,ahub->clocks);if(ret)returnret;+ret=reset_control_reset(ahub->reset);+if(ret){+clk_bulk_disable_unprepare(ahub->nclocks,ahub->clocks);+returnret;+}+regcache_cache_only(ahub->regmap_apbif,false);regcache_cache_only(ahub->regmap_ahub,false);
@@ -462,7 +472,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev){conststructof_device_id*match;conststructtegra30_ahub_soc_data*soc_data;-structreset_control*rst;structresource*res0;void__iomem*regs_apbif,*regs_ahub;intret=0;
@@ -475,22 +484,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev)return-EINVAL;soc_data=match->data;-/*-*TheAHUBhostsaregisterbus:the"configlink".Forthisto-*operatecorrectly,alldevicesonthisbusmustbeoutofreset.-*Ensurethathere.-*/-rst=of_reset_control_array_get_exclusive(pdev->dev.of_node);-if(IS_ERR(rst)){-dev_err(&pdev->dev,"Can't get reset: %p\n",rst);-returnPTR_ERR(rst);-}--ret=reset_control_deassert(rst);-reset_control_put(rst);-if(ret)-returnret;-ahub=devm_kzalloc(&pdev->dev,sizeof(structtegra30_ahub),GFP_KERNEL);if(!ahub)
@@ -507,6 +500,12 @@ static int tegra30_ahub_probe(struct platform_device *pdev)if(ret)returnret;+ahub->reset=devm_reset_control_array_get_exclusive(&pdev->dev);+if(IS_ERR(ahub->reset)){+dev_err(&pdev->dev,"Can't get reset: %p\n",ahub->reset);
I didn't notice that the prior patch already introduced this, but I'd
prefer for this to either be %pe so that the symbolic error name is
printed, or %ld with PTR_ERR(ahub->reset) to format this in a more
standard way that can be more easily grepped for and parsed.
It also seems like the prior patch that converts this to use
of_reset_control_array_get_exclusive() is a bit pointless now. Why not
just move to this directly instead?
Thierry
Originally the code did this in this order: "hda", "hda2codec_2x" and
"hda2hdmi". I don't expect the exact order to be very relevant, but was
there any particular reason to change it?
The reason was "to make code look nicer". This was a conscious decision
since indeed the clocks order shouldn't matter for this driver.
On Tue, Jan 12, 2021 at 03:58:31PM +0300, Dmitry Osipenko wrote:
quoted
Reset hardware in order to bring it into a predictable state.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/pci/hda/hda_tegra.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
@@ -167,6 +169,12 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;+if(!(chip&&chip->running)){
Isn't that check for !chip a bit redundant? If that pointer isn't valid,
we're just going to go crash when dereferencing hda later on, so I think
this can simply be:
if (!chip->running)
I guess you took this from the inverse check below, but I think we can
also drop it from there, perhaps in a separate patch.
@@ -176,6 +184,10 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) /* disable controller wake up event*/ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);+ } else {+ rc = reset_control_reset(hda->reset);
The "if (chip)" part definitely doesn't make sense after this anymore
because now if chip == NULL, then we end up in here and dereference an
invalid "hda" pointer.
Okay, I took a note for the v3.
Also, why reset_control_reset() here? We'll reach this if we ran
reset_control_assert() above, so this should just be
reset_control_deassert() to undo that, right? I suppose it wouldn't hurt
to put throw that standard usleep_range() in there as well that we use
to wait between reset assert and deassert to make sure the clocks have
stabilized and the reset has indeed propagated through the whole IP.
The reset_control_reset() does the delaying before the deassert, i.e. it
does assert -> udelay(1) -> deassert.
https://elixir.free-electrons.com/linux/v5.11-rc3/source/drivers/clk/tegra/clk.c#L133
The reset_control_reset() usage appears to be a bit more code-tidy
variant in comparison to delaying directly. But I don't mind to use
delay + reset_control_deassert() directly since it may not be obvious to
everyone what reset_control_reset() does.
I'll change it in v3.
On Tue, Jan 12, 2021 at 03:58:32PM +0300, Dmitry Osipenko wrote:
quoted
Some of resets are erroneously missed in the configlink_mods[], like APBIF
for example. Use of_reset_control_array_get_exclusive() which requests all
the resets. The problem was hidden by the clk driver which implicitly
de-asserts the missing resets.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 66 +++++-----------------------------
sound/soc/tegra/tegra30_ahub.h | 1 -
2 files changed, 9 insertions(+), 58 deletions(-)
Doing it this way is slightly suboptimal because now we don't actually
have a way of checking that the DT has all the necessary resets listed.
Can we not just make the list complete instead to keep the checks in
place? That should be a much smaller patch, too.
On Tue, Jan 12, 2021 at 03:58:34PM +0300, Dmitry Osipenko wrote:
quoted
Assert hardware reset before clocks are enabled and then de-assert it
after clocks are enabled. This brings hardware into a predictable state
and removes relying on implicit de-assertion of resets which is done by
the clk driver.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 33 ++++++++++++++++-----------------
sound/soc/tegra/tegra30_ahub.h | 1 +
2 files changed, 17 insertions(+), 17 deletions(-)
@@ -65,10 +65,20 @@ static int tegra30_ahub_runtime_resume(struct device *dev){intret;+ret=reset_control_assert(ahub->reset);+if(ret)+returnret;+ret=clk_bulk_prepare_enable(ahub->nclocks,ahub->clocks);if(ret)returnret;+ret=reset_control_reset(ahub->reset);+if(ret){+clk_bulk_disable_unprepare(ahub->nclocks,ahub->clocks);+returnret;+}+regcache_cache_only(ahub->regmap_apbif,false);regcache_cache_only(ahub->regmap_ahub,false);
@@ -462,7 +472,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev){conststructof_device_id*match;conststructtegra30_ahub_soc_data*soc_data;-structreset_control*rst;structresource*res0;void__iomem*regs_apbif,*regs_ahub;intret=0;
@@ -475,22 +484,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev)return-EINVAL;soc_data=match->data;-/*-*TheAHUBhostsaregisterbus:the"configlink".Forthisto-*operatecorrectly,alldevicesonthisbusmustbeoutofreset.-*Ensurethathere.-*/-rst=of_reset_control_array_get_exclusive(pdev->dev.of_node);-if(IS_ERR(rst)){-dev_err(&pdev->dev,"Can't get reset: %p\n",rst);-returnPTR_ERR(rst);-}--ret=reset_control_deassert(rst);-reset_control_put(rst);-if(ret)-returnret;-ahub=devm_kzalloc(&pdev->dev,sizeof(structtegra30_ahub),GFP_KERNEL);if(!ahub)
@@ -507,6 +500,12 @@ static int tegra30_ahub_probe(struct platform_device *pdev)if(ret)returnret;+ahub->reset=devm_reset_control_array_get_exclusive(&pdev->dev);+if(IS_ERR(ahub->reset)){+dev_err(&pdev->dev,"Can't get reset: %p\n",ahub->reset);
I didn't notice that the prior patch already introduced this, but I'd
prefer for this to either be %pe so that the symbolic error name is
printed, or %ld with PTR_ERR(ahub->reset) to format this in a more
standard way that can be more easily grepped for and parsed.
This is already fixed in v2. Good catch anyways, thanks.
It also seems like the prior patch that converts this to use
of_reset_control_array_get_exclusive() is a bit pointless now. Why not
just move to this directly instead?
These are two independent changes. The previous patch fixed the missing
resets, this patch changes the hardware initialization logic.
On Mon, Jan 18, 2021 at 02:39:37AM +0300, Dmitry Osipenko wrote:
15.01.2021 18:35, Thierry Reding пишет:
quoted
On Tue, Jan 12, 2021 at 03:58:31PM +0300, Dmitry Osipenko wrote:
quoted
Reset hardware in order to bring it into a predictable state.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/pci/hda/hda_tegra.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
@@ -167,6 +169,12 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)structhda_tegra*hda=container_of(chip,structhda_tegra,chip);intrc;+if(!(chip&&chip->running)){
Isn't that check for !chip a bit redundant? If that pointer isn't valid,
we're just going to go crash when dereferencing hda later on, so I think
this can simply be:
if (!chip->running)
I guess you took this from the inverse check below, but I think we can
also drop it from there, perhaps in a separate patch.
@@ -176,6 +184,10 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) /* disable controller wake up event*/ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);+ } else {+ rc = reset_control_reset(hda->reset);
The "if (chip)" part definitely doesn't make sense after this anymore
because now if chip == NULL, then we end up in here and dereference an
invalid "hda" pointer.
Okay, I took a note for the v3.
quoted
Also, why reset_control_reset() here? We'll reach this if we ran
reset_control_assert() above, so this should just be
reset_control_deassert() to undo that, right? I suppose it wouldn't hurt
to put throw that standard usleep_range() in there as well that we use
to wait between reset assert and deassert to make sure the clocks have
stabilized and the reset has indeed propagated through the whole IP.
The reset_control_reset() does the delaying before the deassert, i.e. it
does assert -> udelay(1) -> deassert.
https://elixir.free-electrons.com/linux/v5.11-rc3/source/drivers/clk/tegra/clk.c#L133
The reset_control_reset() usage appears to be a bit more code-tidy
variant in comparison to delaying directly. But I don't mind to use
delay + reset_control_deassert() directly since it may not be obvious to
everyone what reset_control_reset() does.
I'll change it in v3.
Thanks. I know that manually having to add the delay everywhere seems a
bit tedious, but I like the way we very explicitly only ever do reset
assert and deassert, rather than the combined reset pulse, because the
latter can give the impression that the device isn't actually in reset
when we do reset_control_reset().
Thierry
Originally the code did this in this order: "hda", "hda2codec_2x" and
"hda2hdmi". I don't expect the exact order to be very relevant, but was
there any particular reason to change it?
The reason was "to make code look nicer". This was a conscious decision
since indeed the clocks order shouldn't matter for this driver.
Yeah, it's probably fine. In case this ends up causing trouble after all
we can always change the order back.
Thierry
On Mon, Jan 18, 2021 at 03:02:38AM +0300, Dmitry Osipenko wrote:
15.01.2021 18:44, Thierry Reding пишет:
quoted
On Tue, Jan 12, 2021 at 03:58:34PM +0300, Dmitry Osipenko wrote:
quoted
Assert hardware reset before clocks are enabled and then de-assert it
after clocks are enabled. This brings hardware into a predictable state
and removes relying on implicit de-assertion of resets which is done by
the clk driver.
Tested-by: Peter Geis <redacted>
Tested-by: Nicolas Chauvet <redacted>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
sound/soc/tegra/tegra30_ahub.c | 33 ++++++++++++++++-----------------
sound/soc/tegra/tegra30_ahub.h | 1 +
2 files changed, 17 insertions(+), 17 deletions(-)
@@ -65,10 +65,20 @@ static int tegra30_ahub_runtime_resume(struct device *dev){intret;+ret=reset_control_assert(ahub->reset);+if(ret)+returnret;+ret=clk_bulk_prepare_enable(ahub->nclocks,ahub->clocks);if(ret)returnret;+ret=reset_control_reset(ahub->reset);+if(ret){+clk_bulk_disable_unprepare(ahub->nclocks,ahub->clocks);+returnret;+}+regcache_cache_only(ahub->regmap_apbif,false);regcache_cache_only(ahub->regmap_ahub,false);
@@ -462,7 +472,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev){conststructof_device_id*match;conststructtegra30_ahub_soc_data*soc_data;-structreset_control*rst;structresource*res0;void__iomem*regs_apbif,*regs_ahub;intret=0;
@@ -475,22 +484,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev)return-EINVAL;soc_data=match->data;-/*-*TheAHUBhostsaregisterbus:the"configlink".Forthisto-*operatecorrectly,alldevicesonthisbusmustbeoutofreset.-*Ensurethathere.-*/-rst=of_reset_control_array_get_exclusive(pdev->dev.of_node);-if(IS_ERR(rst)){-dev_err(&pdev->dev,"Can't get reset: %p\n",rst);-returnPTR_ERR(rst);-}--ret=reset_control_deassert(rst);-reset_control_put(rst);-if(ret)-returnret;-ahub=devm_kzalloc(&pdev->dev,sizeof(structtegra30_ahub),GFP_KERNEL);if(!ahub)
@@ -507,6 +500,12 @@ static int tegra30_ahub_probe(struct platform_device *pdev)if(ret)returnret;+ahub->reset=devm_reset_control_array_get_exclusive(&pdev->dev);+if(IS_ERR(ahub->reset)){+dev_err(&pdev->dev,"Can't get reset: %p\n",ahub->reset);
I didn't notice that the prior patch already introduced this, but I'd
prefer for this to either be %pe so that the symbolic error name is
printed, or %ld with PTR_ERR(ahub->reset) to format this in a more
standard way that can be more easily grepped for and parsed.
This is already fixed in v2. Good catch anyways, thanks.
quoted
It also seems like the prior patch that converts this to use
of_reset_control_array_get_exclusive() is a bit pointless now. Why not
just move to this directly instead?
These are two independent changes. The previous patch fixed the missing
resets, this patch changes the hardware initialization logic.
But moving to devm_reset_control_array_get_exclusive() isn't really part
of the hardware initialization logic change, right? So it's not strictly
related to the rest of this patch.
Anyway, I don't feel strongly about it being part of this patch, so feel
free to keep it here.
Thierry