Commit 7cfefab6 authored by Jerome Brunet's avatar Jerome Brunet

clk: meson: axg-audio: add g12a reset support

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.
Reviewed-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Signed-off-by: default avatarJerome Brunet <jbrunet@baylibre.com>
parent 1d7cedbd
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/reset-controller.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "axg-audio.h" #include "axg-audio.h"
...@@ -918,6 +919,84 @@ static int devm_clk_get_enable(struct device *dev, char *id) ...@@ -918,6 +919,84 @@ static int devm_clk_get_enable(struct device *dev, char *id)
return 0; 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 = { static const struct regmap_config axg_audio_regmap_cfg = {
.reg_bits = 32, .reg_bits = 32,
.val_bits = 32, .val_bits = 32,
...@@ -927,12 +1006,15 @@ static const struct regmap_config axg_audio_regmap_cfg = { ...@@ -927,12 +1006,15 @@ static const struct regmap_config axg_audio_regmap_cfg = {
struct audioclk_data { struct audioclk_data {
struct clk_hw_onecell_data *hw_onecell_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) static int axg_audio_clkc_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const struct audioclk_data *data; const struct audioclk_data *data;
struct axg_audio_reset_data *rst;
struct regmap *map; struct regmap *map;
struct resource *res; struct resource *res;
void __iomem *regs; void __iomem *regs;
...@@ -984,8 +1066,27 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) ...@@ -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, ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
data->hw_onecell_data); 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 = { static const struct audioclk_data axg_audioclk_data = {
...@@ -994,6 +1095,8 @@ 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 = { static const struct audioclk_data g12a_audioclk_data = {
.hw_onecell_data = &g12a_audio_hw_onecell_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[] = { static const struct of_device_id clkc_match_table[] = {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define AUDIO_MCLK_F_CTRL 0x018 #define AUDIO_MCLK_F_CTRL 0x018
#define AUDIO_MST_PAD_CTRL0 0x01c #define AUDIO_MST_PAD_CTRL0 0x01c
#define AUDIO_MST_PAD_CTRL1 0x020 #define AUDIO_MST_PAD_CTRL1 0x020
#define AUDIO_SW_RESET 0x024
#define AUDIO_MST_A_SCLK_CTRL0 0x040 #define AUDIO_MST_A_SCLK_CTRL0 0x040
#define AUDIO_MST_A_SCLK_CTRL1 0x044 #define AUDIO_MST_A_SCLK_CTRL1 0x044
#define AUDIO_MST_B_SCLK_CTRL0 0x048 #define AUDIO_MST_B_SCLK_CTRL0 0x048
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment