forked from Minki/linux
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: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
This commit is contained in:
parent
1d7cedbdfd
commit
7cfefab656
@ -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[] = {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user