Merge tag 'clk-meson-v6.13-1' of https://github.com/BayLibre/clk-meson into clk-amlogic
Pull Amlogic clk driver updates from Jerome Brunet: - Fix hifi_pll rate calculation on Amlogic s4 and c3 - Move audio reset implementation from the Amlogic axg-audio clock controller driver to the reset directory, using the auxiliary device bus - Remove the unnecessary spinlock in the Amlogic mpll driver - Fix Amlogic meson8 clock controller DT bindings * tag 'clk-meson-v6.13-1' of https://github.com/BayLibre/clk-meson: clk: amlogic: axg-audio: use the auxiliary reset driver reset: amlogic: Fix small whitespace issue reset: amlogic: add auxiliary reset driver support reset: amlogic: split the device core and platform probe reset: amlogic: move drivers to a dedicated directory reset: amlogic: add reset status support reset: amlogic: use reset number instead of register count reset: amlogic: add driver parameters reset: amlogic: make parameters unsigned reset: amlogic: use generic data matching function reset: amlogic: convert driver to regmap dt-bindings: clock: convert amlogic,meson8b-clkc.txt to dtschema clk: meson: meson8b: remove spinlock clk: meson: mpll: Delete a useless spinlock from the MPLL clk: meson: s4: pll: fix frac maximum value for hifi_pll clk: meson: c3: pll: fix frac maximum value for hifi_pll clk: meson: Support PLL with fixed fractional denominators clk: meson: s4: pll: hifi_pll support fractional multiplier
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/amlogic,meson8-clkc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic Meson8, Meson8b and Meson8m2 Clock and Reset Controller
|
||||
|
||||
maintainers:
|
||||
- Neil Armstrong <neil.armstrong@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- amlogic,meson8-clkc
|
||||
- amlogic,meson8b-clkc
|
||||
- items:
|
||||
- const: amlogic,meson8m2-clkc
|
||||
- const: amlogic,meson8-clkc
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: xtal
|
||||
- const: ddr_pll
|
||||
- const: clk_32k
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
@@ -1,51 +0,0 @@
|
||||
* Amlogic Meson8, Meson8b and Meson8m2 Clock and Reset Unit
|
||||
|
||||
The Amlogic Meson8 / Meson8b / Meson8m2 clock controller generates and
|
||||
supplies clock to various controllers within the SoC.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: must be one of:
|
||||
- "amlogic,meson8-clkc" for Meson8 (S802) SoCs
|
||||
- "amlogic,meson8b-clkc" for Meson8 (S805) SoCs
|
||||
- "amlogic,meson8m2-clkc" for Meson8m2 (S812) SoCs
|
||||
- #clock-cells: should be 1.
|
||||
- #reset-cells: should be 1.
|
||||
- clocks: list of clock phandles, one for each entry in clock-names
|
||||
- clock-names: should contain the following:
|
||||
* "xtal": the 24MHz system oscillator
|
||||
* "ddr_pll": the DDR PLL clock
|
||||
* "clk_32k": (if present) the 32kHz clock signal from GPIOAO_6 (CLK_32K_IN)
|
||||
|
||||
Parent node should have the following properties :
|
||||
- compatible: "amlogic,meson-hhi-sysctrl", "simple-mfd", "syscon"
|
||||
- reg: base address and size of the HHI system control register space.
|
||||
|
||||
Each clock is assigned an identifier and client nodes can use this identifier
|
||||
to specify the clock which they consume. All available clocks are defined as
|
||||
preprocessor macros in the dt-bindings/clock/meson8b-clkc.h header and can be
|
||||
used in device tree sources.
|
||||
|
||||
Similarly a preprocessor macro for each reset line is defined in
|
||||
dt-bindings/reset/amlogic,meson8b-clkc-reset.h (which can be used from the
|
||||
device tree sources).
|
||||
|
||||
|
||||
Example: Clock controller node:
|
||||
|
||||
clkc: clock-controller {
|
||||
compatible = "amlogic,meson8b-clkc";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
|
||||
Example: UART controller node that consumes the clock generated by the clock
|
||||
controller:
|
||||
|
||||
uart_AO: serial@c81004c0 {
|
||||
compatible = "amlogic,meson-uart";
|
||||
reg = <0xc81004c0 0x14>;
|
||||
interrupts = <0 90 1>;
|
||||
clocks = <&clkc CLKID_CLK81>;
|
||||
};
|
||||
@@ -106,6 +106,7 @@ config COMMON_CLK_AXG_AUDIO
|
||||
select COMMON_CLK_MESON_SCLK_DIV
|
||||
select COMMON_CLK_MESON_CLKC_UTILS
|
||||
select REGMAP_MMIO
|
||||
imply RESET_MESON_AUX
|
||||
help
|
||||
Support for the audio clock controller on AmLogic A113D devices,
|
||||
aka axg, Say Y if you want audio subsystem to work.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <soc/amlogic/reset-meson-aux.h>
|
||||
|
||||
#include "meson-clkc-utils.h"
|
||||
#include "axg-audio.h"
|
||||
#include "clk-regmap.h"
|
||||
@@ -1678,84 +1680,6 @@ static struct clk_regmap *const sm1_clk_regmaps[] = {
|
||||
&sm1_earcrx_dmac_clk,
|
||||
};
|
||||
|
||||
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 struct regmap_config axg_audio_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
@@ -1766,16 +1690,14 @@ struct audioclk_data {
|
||||
struct clk_regmap *const *regmap_clks;
|
||||
unsigned int regmap_clk_num;
|
||||
struct meson_clk_hw_data hw_clks;
|
||||
unsigned int reset_offset;
|
||||
unsigned int reset_num;
|
||||
unsigned int max_register;
|
||||
const char *rst_drvname;
|
||||
};
|
||||
|
||||
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;
|
||||
void __iomem *regs;
|
||||
struct clk_hw *hw;
|
||||
@@ -1834,22 +1756,11 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Stop here if there is no reset */
|
||||
if (!data->reset_num)
|
||||
return 0;
|
||||
/* Register auxiliary reset driver when applicable */
|
||||
if (data->rst_drvname)
|
||||
ret = devm_meson_rst_aux_register(dev, map, data->rst_drvname);
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct audioclk_data axg_audioclk_data = {
|
||||
@@ -1869,9 +1780,8 @@ static const struct audioclk_data g12a_audioclk_data = {
|
||||
.hws = g12a_audio_hw_clks,
|
||||
.num = ARRAY_SIZE(g12a_audio_hw_clks),
|
||||
},
|
||||
.reset_offset = AUDIO_SW_RESET,
|
||||
.reset_num = 26,
|
||||
.max_register = AUDIO_CLK_SPDIFOUT_B_CTRL,
|
||||
.rst_drvname = "rst-g12a",
|
||||
};
|
||||
|
||||
static const struct audioclk_data sm1_audioclk_data = {
|
||||
@@ -1881,9 +1791,8 @@ static const struct audioclk_data sm1_audioclk_data = {
|
||||
.hws = sm1_audio_hw_clks,
|
||||
.num = ARRAY_SIZE(sm1_audio_hw_clks),
|
||||
},
|
||||
.reset_offset = AUDIO_SM1_SW_RESET0,
|
||||
.reset_num = 39,
|
||||
.max_register = AUDIO_EARCRX_DMAC_CLK_CTRL,
|
||||
.rst_drvname = "rst-sm1",
|
||||
};
|
||||
|
||||
static const struct of_device_id clkc_match_table[] = {
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
#include <dt-bindings/clock/axg-clkc.h>
|
||||
|
||||
static DEFINE_SPINLOCK(meson_clk_lock);
|
||||
|
||||
static struct clk_regmap axg_fixed_pll_dco = {
|
||||
.data = &(struct meson_clk_pll_data){
|
||||
.en = {
|
||||
@@ -506,7 +504,6 @@ static struct clk_regmap axg_mpll0_div = {
|
||||
.shift = 0,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
@@ -557,7 +554,6 @@ static struct clk_regmap axg_mpll1_div = {
|
||||
.shift = 1,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
@@ -613,7 +609,6 @@ static struct clk_regmap axg_mpll2_div = {
|
||||
.shift = 2,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
@@ -664,7 +659,6 @@ static struct clk_regmap axg_mpll3_div = {
|
||||
.shift = 3,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
|
||||
@@ -361,6 +361,7 @@ static struct clk_regmap hifi_pll_dco = {
|
||||
.range = &c3_gp0_pll_mult_range,
|
||||
.init_regs = c3_hifi_init_regs,
|
||||
.init_count = ARRAY_SIZE(c3_hifi_init_regs),
|
||||
.frac_max = 100000,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data) {
|
||||
.name = "hifi_pll_dco",
|
||||
|
||||
@@ -112,26 +112,15 @@ static int mpll_set_rate(struct clk_hw *hw,
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
|
||||
unsigned int sdm, n2;
|
||||
unsigned long flags = 0;
|
||||
|
||||
params_from_rate(rate, parent_rate, &sdm, &n2, mpll->flags);
|
||||
|
||||
if (mpll->lock)
|
||||
spin_lock_irqsave(mpll->lock, flags);
|
||||
else
|
||||
__acquire(mpll->lock);
|
||||
|
||||
/* Set the fractional part */
|
||||
meson_parm_write(clk->map, &mpll->sdm, sdm);
|
||||
|
||||
/* Set the integer divider part */
|
||||
meson_parm_write(clk->map, &mpll->n2, n2);
|
||||
|
||||
if (mpll->lock)
|
||||
spin_unlock_irqrestore(mpll->lock, flags);
|
||||
else
|
||||
__release(mpll->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ struct meson_clk_mpll_data {
|
||||
struct parm misc;
|
||||
const struct reg_sequence *init_regs;
|
||||
unsigned int init_count;
|
||||
spinlock_t *lock;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -57,12 +57,13 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
|
||||
struct meson_clk_pll_data *pll)
|
||||
{
|
||||
u64 rate = (u64)parent_rate * m;
|
||||
unsigned int frac_max = pll->frac_max ? pll->frac_max :
|
||||
(1 << pll->frac.width);
|
||||
|
||||
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
|
||||
u64 frac_rate = (u64)parent_rate * frac;
|
||||
|
||||
rate += DIV_ROUND_UP_ULL(frac_rate,
|
||||
(1 << pll->frac.width));
|
||||
rate += DIV_ROUND_UP_ULL(frac_rate, frac_max);
|
||||
}
|
||||
|
||||
return DIV_ROUND_UP_ULL(rate, n);
|
||||
@@ -100,7 +101,8 @@ static unsigned int __pll_params_with_frac(unsigned long rate,
|
||||
unsigned int n,
|
||||
struct meson_clk_pll_data *pll)
|
||||
{
|
||||
unsigned int frac_max = (1 << pll->frac.width);
|
||||
unsigned int frac_max = pll->frac_max ? pll->frac_max :
|
||||
(1 << pll->frac.width);
|
||||
u64 val = (u64)rate * n;
|
||||
|
||||
/* Bail out if we are already over the requested rate */
|
||||
|
||||
@@ -43,6 +43,7 @@ struct meson_clk_pll_data {
|
||||
unsigned int init_count;
|
||||
const struct pll_params_table *table;
|
||||
const struct pll_mult_range *range;
|
||||
unsigned int frac_max;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
|
||||
#include <dt-bindings/clock/g12a-clkc.h>
|
||||
|
||||
static DEFINE_SPINLOCK(meson_clk_lock);
|
||||
|
||||
static struct clk_regmap g12a_fixed_pll_dco = {
|
||||
.data = &(struct meson_clk_pll_data){
|
||||
.en = {
|
||||
@@ -2225,7 +2223,6 @@ static struct clk_regmap g12a_mpll0_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = g12a_mpll0_init_regs,
|
||||
.init_count = ARRAY_SIZE(g12a_mpll0_init_regs),
|
||||
},
|
||||
@@ -2279,7 +2276,6 @@ static struct clk_regmap g12a_mpll1_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = g12a_mpll1_init_regs,
|
||||
.init_count = ARRAY_SIZE(g12a_mpll1_init_regs),
|
||||
},
|
||||
@@ -2333,7 +2329,6 @@ static struct clk_regmap g12a_mpll2_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = g12a_mpll2_init_regs,
|
||||
.init_count = ARRAY_SIZE(g12a_mpll2_init_regs),
|
||||
},
|
||||
@@ -2387,7 +2382,6 @@ static struct clk_regmap g12a_mpll3_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = g12a_mpll3_init_regs,
|
||||
.init_count = ARRAY_SIZE(g12a_mpll3_init_regs),
|
||||
},
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include <dt-bindings/clock/gxbb-clkc.h>
|
||||
|
||||
static DEFINE_SPINLOCK(meson_clk_lock);
|
||||
|
||||
static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
|
||||
PLL_PARAMS(32, 1),
|
||||
PLL_PARAMS(33, 1),
|
||||
@@ -731,7 +729,6 @@ static struct clk_regmap gxbb_mpll0_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll0_div",
|
||||
@@ -760,7 +757,6 @@ static struct clk_regmap gxl_mpll0_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll0_div",
|
||||
@@ -812,7 +808,6 @@ static struct clk_regmap gxbb_mpll1_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll1_div",
|
||||
@@ -855,7 +850,6 @@ static struct clk_regmap gxbb_mpll2_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll2_div",
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <dt-bindings/clock/meson8b-clkc.h>
|
||||
#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h>
|
||||
|
||||
static DEFINE_SPINLOCK(meson_clk_lock);
|
||||
|
||||
struct meson8b_clk_reset {
|
||||
struct reset_controller_dev reset;
|
||||
struct regmap *regmap;
|
||||
@@ -492,7 +490,6 @@ static struct clk_regmap meson8b_mpll0_div = {
|
||||
.shift = 25,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll0_div",
|
||||
@@ -537,7 +534,6 @@ static struct clk_regmap meson8b_mpll1_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll1_div",
|
||||
@@ -582,7 +578,6 @@ static struct clk_regmap meson8b_mpll2_div = {
|
||||
.shift = 16,
|
||||
.width = 9,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "mpll2_div",
|
||||
@@ -3702,7 +3697,6 @@ static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev,
|
||||
container_of(rcdev, struct meson8b_clk_reset, reset);
|
||||
const struct meson8b_clk_reset_line *reset;
|
||||
unsigned int value = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (id >= ARRAY_SIZE(meson8b_clk_reset_bits))
|
||||
return -EINVAL;
|
||||
@@ -3712,13 +3706,9 @@ static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev,
|
||||
if (assert != reset->active_low)
|
||||
value = BIT(reset->bit_idx);
|
||||
|
||||
spin_lock_irqsave(&meson_clk_lock, flags);
|
||||
|
||||
regmap_update_bits(meson8b_clk_reset->regmap, reset->reg,
|
||||
BIT(reset->bit_idx), value);
|
||||
|
||||
spin_unlock_irqrestore(&meson_clk_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include "meson-clkc-utils.h"
|
||||
#include <dt-bindings/clock/amlogic,s4-pll-clkc.h>
|
||||
|
||||
static DEFINE_SPINLOCK(meson_clk_lock);
|
||||
|
||||
/*
|
||||
* These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
|
||||
* The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
|
||||
@@ -329,7 +327,6 @@ static struct clk_regmap s4_gp0_pll = {
|
||||
* Internal hifi pll emulation configuration parameters
|
||||
*/
|
||||
static const struct reg_sequence s4_hifi_init_regs[] = {
|
||||
{ .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 },
|
||||
{ .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 },
|
||||
{ .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 },
|
||||
{ .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 },
|
||||
@@ -354,6 +351,11 @@ static struct clk_regmap s4_hifi_pll_dco = {
|
||||
.shift = 10,
|
||||
.width = 5,
|
||||
},
|
||||
.frac = {
|
||||
.reg_off = ANACTRL_HIFIPLL_CTRL1,
|
||||
.shift = 0,
|
||||
.width = 17,
|
||||
},
|
||||
.l = {
|
||||
.reg_off = ANACTRL_HIFIPLL_CTRL0,
|
||||
.shift = 31,
|
||||
@@ -367,6 +369,7 @@ static struct clk_regmap s4_hifi_pll_dco = {
|
||||
.range = &s4_gp0_pll_mult_range,
|
||||
.init_regs = s4_hifi_init_regs,
|
||||
.init_count = ARRAY_SIZE(s4_hifi_init_regs),
|
||||
.frac_max = 100000,
|
||||
.flags = CLK_MESON_PLL_ROUND_CLOSEST,
|
||||
},
|
||||
.hw.init = &(struct clk_init_data){
|
||||
@@ -542,7 +545,6 @@ static struct clk_regmap s4_mpll0_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = s4_mpll0_init_regs,
|
||||
.init_count = ARRAY_SIZE(s4_mpll0_init_regs),
|
||||
},
|
||||
@@ -596,7 +598,6 @@ static struct clk_regmap s4_mpll1_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = s4_mpll1_init_regs,
|
||||
.init_count = ARRAY_SIZE(s4_mpll1_init_regs),
|
||||
},
|
||||
@@ -650,7 +651,6 @@ static struct clk_regmap s4_mpll2_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = s4_mpll2_init_regs,
|
||||
.init_count = ARRAY_SIZE(s4_mpll2_init_regs),
|
||||
},
|
||||
@@ -704,7 +704,6 @@ static struct clk_regmap s4_mpll3_div = {
|
||||
.shift = 29,
|
||||
.width = 1,
|
||||
},
|
||||
.lock = &meson_clk_lock,
|
||||
.init_regs = s4_mpll3_init_regs,
|
||||
.init_count = ARRAY_SIZE(s4_mpll3_init_regs),
|
||||
},
|
||||
|
||||
+1
-14
@@ -153,20 +153,6 @@ config RESET_MCHP_SPARX5
|
||||
help
|
||||
This driver supports switch core reset for the Microchip Sparx5 SoC.
|
||||
|
||||
config RESET_MESON
|
||||
tristate "Meson Reset Driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
default ARCH_MESON
|
||||
help
|
||||
This enables the reset driver for Amlogic Meson SoCs.
|
||||
|
||||
config RESET_MESON_AUDIO_ARB
|
||||
tristate "Meson Audio Memory Arbiter Reset Driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
help
|
||||
This enables the reset driver for Audio Memory Arbiter of
|
||||
Amlogic's A113 based SoCs
|
||||
|
||||
config RESET_NPCM
|
||||
bool "NPCM BMC Reset Driver" if COMPILE_TEST
|
||||
default ARCH_NPCM
|
||||
@@ -356,6 +342,7 @@ config RESET_ZYNQMP
|
||||
help
|
||||
This enables the reset controller driver for Xilinx ZynqMP SoCs.
|
||||
|
||||
source "drivers/reset/amlogic/Kconfig"
|
||||
source "drivers/reset/starfive/Kconfig"
|
||||
source "drivers/reset/sti/Kconfig"
|
||||
source "drivers/reset/hisilicon/Kconfig"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += core.o
|
||||
obj-y += amlogic/
|
||||
obj-y += hisilicon/
|
||||
obj-y += starfive/
|
||||
obj-y += sti/
|
||||
@@ -21,8 +22,6 @@ obj-$(CONFIG_RESET_K210) += reset-k210.o
|
||||
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
|
||||
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
|
||||
obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
|
||||
obj-$(CONFIG_RESET_MESON) += reset-meson.o
|
||||
obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
|
||||
obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
|
||||
obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
|
||||
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
config RESET_MESON_COMMON
|
||||
tristate
|
||||
select REGMAP
|
||||
|
||||
config RESET_MESON
|
||||
tristate "Meson Reset Driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
default ARCH_MESON
|
||||
select REGMAP_MMIO
|
||||
select RESET_MESON_COMMON
|
||||
help
|
||||
This enables the reset driver for Amlogic SoCs.
|
||||
|
||||
config RESET_MESON_AUX
|
||||
tristate "Meson Reset Auxiliary Driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
select RESET_MESON_COMMON
|
||||
help
|
||||
This enables the reset auxiliary driver for Amlogic SoCs.
|
||||
|
||||
config RESET_MESON_AUDIO_ARB
|
||||
tristate "Meson Audio Memory Arbiter Reset Driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
help
|
||||
This enables the reset driver for Audio Memory Arbiter of
|
||||
Amlogic's A113 based SoCs
|
||||
@@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_RESET_MESON) += reset-meson.o
|
||||
obj-$(CONFIG_RESET_MESON_AUX) += reset-meson-aux.o
|
||||
obj-$(CONFIG_RESET_MESON_COMMON) += reset-meson-common.o
|
||||
obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Amlogic Meson Reset Auxiliary driver
|
||||
*
|
||||
* Copyright (c) 2024 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "reset-meson.h"
|
||||
#include <soc/amlogic/reset-meson-aux.h>
|
||||
|
||||
static DEFINE_IDA(meson_rst_aux_ida);
|
||||
|
||||
struct meson_reset_adev {
|
||||
struct auxiliary_device adev;
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
#define to_meson_reset_adev(_adev) \
|
||||
container_of((_adev), struct meson_reset_adev, adev)
|
||||
|
||||
static const struct meson_reset_param meson_g12a_audio_param = {
|
||||
.reset_ops = &meson_reset_toggle_ops,
|
||||
.reset_num = 26,
|
||||
.level_offset = 0x24,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_sm1_audio_param = {
|
||||
.reset_ops = &meson_reset_toggle_ops,
|
||||
.reset_num = 39,
|
||||
.level_offset = 0x28,
|
||||
};
|
||||
|
||||
static const struct auxiliary_device_id meson_reset_aux_ids[] = {
|
||||
{
|
||||
.name = "axg-audio-clkc.rst-g12a",
|
||||
.driver_data = (kernel_ulong_t)&meson_g12a_audio_param,
|
||||
}, {
|
||||
.name = "axg-audio-clkc.rst-sm1",
|
||||
.driver_data = (kernel_ulong_t)&meson_sm1_audio_param,
|
||||
}, {}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, meson_reset_aux_ids);
|
||||
|
||||
static int meson_reset_aux_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
const struct meson_reset_param *param =
|
||||
(const struct meson_reset_param *)(id->driver_data);
|
||||
struct meson_reset_adev *raux =
|
||||
to_meson_reset_adev(adev);
|
||||
|
||||
return meson_reset_controller_register(&adev->dev, raux->map, param);
|
||||
}
|
||||
|
||||
static struct auxiliary_driver meson_reset_aux_driver = {
|
||||
.probe = meson_reset_aux_probe,
|
||||
.id_table = meson_reset_aux_ids,
|
||||
};
|
||||
module_auxiliary_driver(meson_reset_aux_driver);
|
||||
|
||||
static void meson_rst_aux_release(struct device *dev)
|
||||
{
|
||||
struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
||||
struct meson_reset_adev *raux =
|
||||
to_meson_reset_adev(adev);
|
||||
|
||||
ida_free(&meson_rst_aux_ida, adev->id);
|
||||
kfree(raux);
|
||||
}
|
||||
|
||||
static void meson_rst_aux_unregister_adev(void *_adev)
|
||||
{
|
||||
struct auxiliary_device *adev = _adev;
|
||||
|
||||
auxiliary_device_delete(adev);
|
||||
auxiliary_device_uninit(adev);
|
||||
}
|
||||
|
||||
int devm_meson_rst_aux_register(struct device *dev,
|
||||
struct regmap *map,
|
||||
const char *adev_name)
|
||||
{
|
||||
struct meson_reset_adev *raux;
|
||||
struct auxiliary_device *adev;
|
||||
int ret;
|
||||
|
||||
raux = kzalloc(sizeof(*raux), GFP_KERNEL);
|
||||
if (!raux)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ida_alloc(&meson_rst_aux_ida, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto raux_free;
|
||||
|
||||
raux->map = map;
|
||||
|
||||
adev = &raux->adev;
|
||||
adev->id = ret;
|
||||
adev->name = adev_name;
|
||||
adev->dev.parent = dev;
|
||||
adev->dev.release = meson_rst_aux_release;
|
||||
device_set_of_node_from_dev(&adev->dev, dev);
|
||||
|
||||
ret = auxiliary_device_init(adev);
|
||||
if (ret)
|
||||
goto ida_free;
|
||||
|
||||
ret = __auxiliary_device_add(adev, dev->driver->name);
|
||||
if (ret) {
|
||||
auxiliary_device_uninit(adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(dev, meson_rst_aux_unregister_adev,
|
||||
adev);
|
||||
|
||||
ida_free:
|
||||
ida_free(&meson_rst_aux_ida, adev->id);
|
||||
raux_free:
|
||||
kfree(raux);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_meson_rst_aux_register);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson Reset Auxiliary driver");
|
||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_IMPORT_NS(MESON_RESET);
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Amlogic Meson Reset core functions
|
||||
*
|
||||
* Copyright (c) 2016-2024 BayLibre, SAS.
|
||||
* Authors: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "reset-meson.h"
|
||||
|
||||
struct meson_reset {
|
||||
const struct meson_reset_param *param;
|
||||
struct reset_controller_dev rcdev;
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
static void meson_reset_offset_and_bit(struct meson_reset *data,
|
||||
unsigned long id,
|
||||
unsigned int *offset,
|
||||
unsigned int *bit)
|
||||
{
|
||||
unsigned int stride = regmap_get_reg_stride(data->map);
|
||||
|
||||
*offset = (id / (stride * BITS_PER_BYTE)) * stride;
|
||||
*bit = id % (stride * BITS_PER_BYTE);
|
||||
}
|
||||
|
||||
static int meson_reset_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct meson_reset *data =
|
||||
container_of(rcdev, struct meson_reset, rcdev);
|
||||
unsigned int offset, bit;
|
||||
|
||||
meson_reset_offset_and_bit(data, id, &offset, &bit);
|
||||
offset += data->param->reset_offset;
|
||||
|
||||
return regmap_write(data->map, offset, BIT(bit));
|
||||
}
|
||||
|
||||
static int meson_reset_level(struct reset_controller_dev *rcdev,
|
||||
unsigned long id, bool assert)
|
||||
{
|
||||
struct meson_reset *data =
|
||||
container_of(rcdev, struct meson_reset, rcdev);
|
||||
unsigned int offset, bit;
|
||||
|
||||
meson_reset_offset_and_bit(data, id, &offset, &bit);
|
||||
offset += data->param->level_offset;
|
||||
assert ^= data->param->level_low_reset;
|
||||
|
||||
return regmap_update_bits(data->map, offset,
|
||||
BIT(bit), assert ? BIT(bit) : 0);
|
||||
}
|
||||
|
||||
static int meson_reset_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct meson_reset *data =
|
||||
container_of(rcdev, struct meson_reset, rcdev);
|
||||
unsigned int val, offset, bit;
|
||||
|
||||
meson_reset_offset_and_bit(data, id, &offset, &bit);
|
||||
offset += data->param->level_offset;
|
||||
|
||||
regmap_read(data->map, offset, &val);
|
||||
val = !!(BIT(bit) & val);
|
||||
|
||||
return val ^ data->param->level_low_reset;
|
||||
}
|
||||
|
||||
static int meson_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return meson_reset_level(rcdev, id, true);
|
||||
}
|
||||
|
||||
static int meson_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return meson_reset_level(rcdev, id, false);
|
||||
}
|
||||
|
||||
static int meson_reset_level_toggle(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = meson_reset_assert(rcdev, id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return meson_reset_deassert(rcdev, id);
|
||||
}
|
||||
|
||||
const struct reset_control_ops meson_reset_ops = {
|
||||
.reset = meson_reset_reset,
|
||||
.assert = meson_reset_assert,
|
||||
.deassert = meson_reset_deassert,
|
||||
.status = meson_reset_status,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(meson_reset_ops, MESON_RESET);
|
||||
|
||||
const struct reset_control_ops meson_reset_toggle_ops = {
|
||||
.reset = meson_reset_level_toggle,
|
||||
.assert = meson_reset_assert,
|
||||
.deassert = meson_reset_deassert,
|
||||
.status = meson_reset_status,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(meson_reset_toggle_ops, MESON_RESET);
|
||||
|
||||
int meson_reset_controller_register(struct device *dev, struct regmap *map,
|
||||
const struct meson_reset_param *param)
|
||||
{
|
||||
struct meson_reset *data;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->param = param;
|
||||
data->map = map;
|
||||
data->rcdev.owner = dev->driver->owner;
|
||||
data->rcdev.nr_resets = param->reset_num;
|
||||
data->rcdev.ops = data->param->reset_ops;
|
||||
data->rcdev.of_node = dev->of_node;
|
||||
|
||||
return devm_reset_controller_register(dev, &data->rcdev);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(meson_reset_controller_register, MESON_RESET);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson Reset Core function");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_IMPORT_NS(MESON_RESET);
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Amlogic Meson Reset Controller driver
|
||||
*
|
||||
* Copyright (c) 2016-2024 BayLibre, SAS.
|
||||
* Authors: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "reset-meson.h"
|
||||
|
||||
static const struct meson_reset_param meson8b_param = {
|
||||
.reset_ops = &meson_reset_ops,
|
||||
.reset_num = 256,
|
||||
.reset_offset = 0x0,
|
||||
.level_offset = 0x7c,
|
||||
.level_low_reset = true,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_a1_param = {
|
||||
.reset_ops = &meson_reset_ops,
|
||||
.reset_num = 96,
|
||||
.reset_offset = 0x0,
|
||||
.level_offset = 0x40,
|
||||
.level_low_reset = true,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_s4_param = {
|
||||
.reset_ops = &meson_reset_ops,
|
||||
.reset_num = 192,
|
||||
.reset_offset = 0x0,
|
||||
.level_offset = 0x40,
|
||||
.level_low_reset = true,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param t7_param = {
|
||||
.reset_num = 224,
|
||||
.reset_offset = 0x0,
|
||||
.level_offset = 0x40,
|
||||
.level_low_reset = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
|
||||
{ .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,c3-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,t7-reset", .data = &t7_param},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
static int meson_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_reset_param *param;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regmap *map;
|
||||
void __iomem *base;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
param = device_get_match_data(dev);
|
||||
if (!param)
|
||||
return -ENODEV;
|
||||
|
||||
map = devm_regmap_init_mmio(dev, base, ®map_config);
|
||||
if (IS_ERR(map))
|
||||
return dev_err_probe(dev, PTR_ERR(map),
|
||||
"can't init regmap mmio region\n");
|
||||
|
||||
return meson_reset_controller_register(dev, map, param);
|
||||
}
|
||||
|
||||
static struct platform_driver meson_reset_driver = {
|
||||
.probe = meson_reset_probe,
|
||||
.driver = {
|
||||
.name = "meson_reset",
|
||||
.of_match_table = meson_reset_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(meson_reset_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_IMPORT_NS(MESON_RESET);
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (c) 2024 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_RESET_H
|
||||
#define __MESON_RESET_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
struct meson_reset_param {
|
||||
const struct reset_control_ops *reset_ops;
|
||||
unsigned int reset_num;
|
||||
unsigned int reset_offset;
|
||||
unsigned int level_offset;
|
||||
bool level_low_reset;
|
||||
};
|
||||
|
||||
int meson_reset_controller_register(struct device *dev, struct regmap *map,
|
||||
const struct meson_reset_param *param);
|
||||
|
||||
extern const struct reset_control_ops meson_reset_ops;
|
||||
extern const struct reset_control_ops meson_reset_toggle_ops;
|
||||
|
||||
#endif /* __MESON_RESET_H */
|
||||
@@ -1,159 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Amlogic Meson Reset Controller driver
|
||||
*
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BITS_PER_REG 32
|
||||
|
||||
struct meson_reset_param {
|
||||
int reg_count;
|
||||
int level_offset;
|
||||
};
|
||||
|
||||
struct meson_reset {
|
||||
void __iomem *reg_base;
|
||||
const struct meson_reset_param *param;
|
||||
struct reset_controller_dev rcdev;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int meson_reset_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct meson_reset *data =
|
||||
container_of(rcdev, struct meson_reset, rcdev);
|
||||
unsigned int bank = id / BITS_PER_REG;
|
||||
unsigned int offset = id % BITS_PER_REG;
|
||||
void __iomem *reg_addr = data->reg_base + (bank << 2);
|
||||
|
||||
writel(BIT(offset), reg_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_reset_level(struct reset_controller_dev *rcdev,
|
||||
unsigned long id, bool assert)
|
||||
{
|
||||
struct meson_reset *data =
|
||||
container_of(rcdev, struct meson_reset, rcdev);
|
||||
unsigned int bank = id / BITS_PER_REG;
|
||||
unsigned int offset = id % BITS_PER_REG;
|
||||
void __iomem *reg_addr;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
reg_addr = data->reg_base + data->param->level_offset + (bank << 2);
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
reg = readl(reg_addr);
|
||||
if (assert)
|
||||
writel(reg & ~BIT(offset), reg_addr);
|
||||
else
|
||||
writel(reg | BIT(offset), reg_addr);
|
||||
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return meson_reset_level(rcdev, id, true);
|
||||
}
|
||||
|
||||
static int meson_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
return meson_reset_level(rcdev, id, false);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops meson_reset_ops = {
|
||||
.reset = meson_reset_reset,
|
||||
.assert = meson_reset_assert,
|
||||
.deassert = meson_reset_deassert,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson8b_param = {
|
||||
.reg_count = 8,
|
||||
.level_offset = 0x7c,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_a1_param = {
|
||||
.reg_count = 3,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_s4_param = {
|
||||
.reg_count = 6,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param t7_param = {
|
||||
.reg_count = 7,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
|
||||
{ .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,c3-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,t7-reset", .data = &t7_param},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
|
||||
|
||||
static int meson_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_reset *data;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(data->reg_base))
|
||||
return PTR_ERR(data->reg_base);
|
||||
|
||||
data->param = of_device_get_match_data(&pdev->dev);
|
||||
if (!data->param)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG;
|
||||
data->rcdev.ops = &meson_reset_ops;
|
||||
data->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static struct platform_driver meson_reset_driver = {
|
||||
.probe = meson_reset_probe,
|
||||
.driver = {
|
||||
.name = "meson_reset",
|
||||
.of_match_table = meson_reset_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(meson_reset_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __SOC_RESET_MESON_AUX_H
|
||||
#define __SOC_RESET_MESON_AUX_H
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
struct device;
|
||||
struct regmap;
|
||||
|
||||
#if IS_ENABLED(CONFIG_RESET_MESON_AUX)
|
||||
int devm_meson_rst_aux_register(struct device *dev,
|
||||
struct regmap *map,
|
||||
const char *adev_name);
|
||||
#else
|
||||
static inline int devm_meson_rst_aux_register(struct device *dev,
|
||||
struct regmap *map,
|
||||
const char *adev_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SOC_RESET_MESON_AUX_H */
|
||||
Reference in New Issue
Block a user