Commit 6b0b8ccf authored by Maxime Ripard's avatar Maxime Ripard

clk: sunxi: Rework MMC phase clocks

Instead of having three different clocks for the main MMC clock and the two
phase sub-clocks, which involved having three different drivers sharing the
same register, rework it to have the same single driver registering three
different clocks.
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Reviewed-by: default avatarChen-Yu Tsai <wens@csie.org>
Tested-by: default avatarChen-Yu Tsai <wens@csie.org>
Acked-by: default avatarMike Turquette <mturquette@linaro.org>
parent 3ec72fab
...@@ -55,8 +55,7 @@ Required properties: ...@@ -55,8 +55,7 @@ Required properties:
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
"allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13
"allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 "allwinner,sun4i-a10-mmc-clk" - for the MMC clock
"allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10
"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
"allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23
"allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks
...@@ -95,6 +94,10 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output ...@@ -95,6 +94,10 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output
is the normal PLL6 output, or "pll6". The second output is rate doubled is the normal PLL6 output, or "pll6". The second output is rate doubled
PLL6, or "pll6x2". PLL6, or "pll6x2".
The "allwinner,sun4i-a10-mmc-clk" has three different outputs: the
main clock, with the ID 0, and the output and sample clocks, with the
IDs 1 and 2, respectively.
For example: For example:
osc24M: clk@01c20050 { osc24M: clk@01c20050 {
...@@ -138,11 +141,11 @@ cpu: cpu@01c20054 { ...@@ -138,11 +141,11 @@ cpu: cpu@01c20054 {
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0", "mmc0_output", "mmc0_sample";
}; };
mii_phy_tx_clk: clk@2 { mii_phy_tx_clk: clk@2 {
......
...@@ -152,14 +152,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node) ...@@ -152,14 +152,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node)
} }
CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup);
struct mmc_phase_data {
u8 offset;
};
struct mmc_phase { struct mmc_phase {
struct clk_hw hw; struct clk_hw hw;
u8 offset;
void __iomem *reg; void __iomem *reg;
struct mmc_phase_data *data;
spinlock_t *lock; spinlock_t *lock;
}; };
...@@ -175,7 +171,7 @@ static int mmc_get_phase(struct clk_hw *hw) ...@@ -175,7 +171,7 @@ static int mmc_get_phase(struct clk_hw *hw)
u8 delay; u8 delay;
value = readl(phase->reg); value = readl(phase->reg);
delay = (value >> phase->data->offset) & 0x3; delay = (value >> phase->offset) & 0x3;
if (!delay) if (!delay)
return 180; return 180;
...@@ -263,8 +259,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees) ...@@ -263,8 +259,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees)
spin_lock_irqsave(phase->lock, flags); spin_lock_irqsave(phase->lock, flags);
value = readl(phase->reg); value = readl(phase->reg);
value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); value &= ~GENMASK(phase->offset + 3, phase->offset);
value |= delay << phase->data->offset; value |= delay << phase->offset;
writel(value, phase->reg); writel(value, phase->reg);
spin_unlock_irqrestore(phase->lock, flags); spin_unlock_irqrestore(phase->lock, flags);
...@@ -276,66 +272,77 @@ static const struct clk_ops mmc_clk_ops = { ...@@ -276,66 +272,77 @@ static const struct clk_ops mmc_clk_ops = {
.set_phase = mmc_set_phase, .set_phase = mmc_set_phase,
}; };
static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, static DEFINE_SPINLOCK(sun4i_a10_mmc_lock);
struct mmc_phase_data *data)
{
const char *parent_names[1] = { of_clk_get_parent_name(node, 0) };
struct clk_init_data init = {
.num_parents = 1,
.parent_names = parent_names,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
struct clk *clk;
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
return;
phase->hw.init = &init;
phase->reg = of_iomap(node, 0); static void __init sun4i_a10_mmc_setup(struct device_node *node)
if (!phase->reg) {
goto err_free; struct clk_onecell_data *clk_data;
const char *parent;
void __iomem *reg;
int i;
phase->data = data; reg = of_io_request_and_map(node, 0, of_node_full_name(node));
phase->lock = &sun4i_a10_mod0_lock; if (IS_ERR(reg)) {
pr_err("Couldn't map the %s clock registers\n", node->name);
return;
}
if (of_property_read_string(node, "clock-output-names", &init.name)) clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
init.name = node->name; if (!clk_data)
return;
clk = clk_register(NULL, &phase->hw); clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL);
if (IS_ERR(clk)) if (!clk_data->clks)
goto err_unmap; goto err_free_data;
clk_data->clk_num = 3;
clk_data->clks[0] = sunxi_factors_register(node,
&sun4i_a10_mod0_data,
&sun4i_a10_mmc_lock, reg);
if (!clk_data->clks[0])
goto err_free_clks;
parent = __clk_get_name(clk_data->clks[0]);
for (i = 1; i < 3; i++) {
struct clk_init_data init = {
.num_parents = 1,
.parent_names = &parent,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
continue;
phase->hw.init = &init;
phase->reg = reg;
phase->lock = &sun4i_a10_mmc_lock;
if (i == 1)
phase->offset = 8;
else
phase->offset = 20;
if (of_property_read_string_index(node, "clock-output-names",
i, &init.name))
init.name = node->name;
clk_data->clks[i] = clk_register(NULL, &phase->hw);
if (IS_ERR(clk_data->clks[i])) {
kfree(phase);
continue;
}
}
of_clk_add_provider(node, of_clk_src_simple_get, clk); of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
return; return;
err_unmap: err_free_clks:
iounmap(phase->reg); kfree(clk_data->clks);
err_free: err_free_data:
kfree(phase); kfree(clk_data);
}
static struct mmc_phase_data mmc_output_clk = {
.offset = 8,
};
static struct mmc_phase_data mmc_sample_clk = {
.offset = 20,
};
static void __init sun4i_a10_mmc_output_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_output_clk);
}
CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup);
static void __init sun4i_a10_mmc_sample_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk);
} }
CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); CLK_OF_DECLARE(sun4i_a10_mmc, "allwinner,sun4i-a10-mmc-clk", sun4i_a10_mmc_setup);
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