Allwinner clocks additions for 4.7
As usual, a bunch of clocks patches for 4.7, mostly fixes and cleanups, and display-related clocks. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXI4/UAAoJEBx+YmzsjxAgJ9sP/iAKVRDJt/ng0Zw+niGC8EMZ AdqXCSbRj7bwnIyT+0ETEFIhaUTXVEg0wIfEQxPKIMOatL51OlR0Iuwviji43IeF oJUDQHSuKOZav7mlLKuEQJIOQBlqbMqjSecFyPN7yqRS9+qD+bDxWE54wXxapXPN QT/RT7662cSUWzmeUFE5OcBd8AIHQp32ozeWFA7kezfExUqQXHopFi98e0erba+x Z74cfC7aNOce3BcgpiyY0UdfHWIGZxkTEFp6KELX92gCrGVghQ+SjOTyp6FIcU/N Y+3BEsjMEByRfhxokKdmReoOAHlHJOmPlERa7vUqsksbEHYtNbbQxRMag76R00hI BdOASfvHcJVSeQQWKgOcdHoHCZcNubgDMjqv0aRY1ZX7MeVg1qxw+XEhsunUTNay Ifjq2qRLY2dGOioUk9adCorr3Z+Of9uPjB1VTQSbrKgJLe7fMdXp8ww6iNxQZ9eo x5JtjaJ2sREFQNGK8WzpImCJ5g6Oa0GQ7C4Lg/Y3obFRnsBiGDKLJDwmbHkhO/ZK i8N7AXIum0k+Tad8ndLLTahKJ4iH46E2RKDbieCzRIW0oqnB+nAzsm4a2SG3AJFR hB11sL3WCugDDJHCQvzCzdadHXSOYJC3g30r/WC7jr6ri0c7IHXQ0OHHhF1d36Ms rzWKUbSyINwTXtjijfiS =ttF/ -----END PGP SIGNATURE----- Merge tag 'sunxi-clocks-for-4.7' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into clk-next Pull Allwinner clock driver updates from Maxime Ripard: As usual, a bunch of clocks patches for 4.7, mostly fixes and cleanups, and display-related clocks. * tag 'sunxi-clocks-for-4.7' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux: clk: sunxi: Let divs clocks read the base factor clock name from devicetree clk: sunxi: Add TCON channel1 clock clk: sunxi: Add PLL3 clock dt-bindings: clk: sun5i: add DRAM gates compatible clk: sunxi: Use resource_size clk: sunxi: Add sun6i/8i display support clk: sunxi: mod1 clock should modify it's parent
This commit is contained in:
commit
51de8760fb
|
@ -10,6 +10,7 @@ Required properties:
|
||||||
"allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
|
"allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
|
||||||
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
|
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
|
||||||
"allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
|
"allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
|
||||||
|
"allwinner,sun4i-a10-pll3-clk" - for the video PLL clock on A10
|
||||||
"allwinner,sun9i-a80-pll4-clk" - for the peripheral PLLs on A80
|
"allwinner,sun9i-a80-pll4-clk" - for the peripheral PLLs on A80
|
||||||
"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
|
"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
|
||||||
"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
|
"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
|
||||||
|
@ -64,6 +65,7 @@ Required properties:
|
||||||
"allwinner,sun8i-h3-bus-gates-clk" - for the bus gates on H3
|
"allwinner,sun8i-h3-bus-gates-clk" - for the bus gates on H3
|
||||||
"allwinner,sun9i-a80-apbs-gates-clk" - for the APBS gates on A80
|
"allwinner,sun9i-a80-apbs-gates-clk" - for the APBS gates on A80
|
||||||
"allwinner,sun4i-a10-dram-gates-clk" - for the DRAM gates on A10
|
"allwinner,sun4i-a10-dram-gates-clk" - for the DRAM gates on A10
|
||||||
|
"allwinner,sun5i-a13-dram-gates-clk" - for the DRAM gates on A13
|
||||||
"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-clk" - for the MMC clock
|
"allwinner,sun4i-a10-mmc-clk" - for the MMC clock
|
||||||
"allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80
|
"allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80
|
||||||
|
@ -73,6 +75,7 @@ Required properties:
|
||||||
"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
|
||||||
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
|
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
|
||||||
|
"allwinner,sun4i-a10-tcon-ch1-clk" - for the TCON channel 1 clock on the A10
|
||||||
"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
|
"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
|
||||||
"allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
|
"allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
|
||||||
"allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31
|
"allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31
|
||||||
|
@ -81,6 +84,7 @@ Required properties:
|
||||||
"allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80
|
"allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80
|
||||||
"allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80
|
"allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80
|
||||||
"allwinner,sun4i-a10-ve-clk" - for the Video Engine clock
|
"allwinner,sun4i-a10-ve-clk" - for the Video Engine clock
|
||||||
|
"allwinner,sun6i-a31-display-clk" - for the display clocks
|
||||||
|
|
||||||
Required properties for all clocks:
|
Required properties for all clocks:
|
||||||
- reg : shall be the control register address for the clock.
|
- reg : shall be the control register address for the clock.
|
||||||
|
|
|
@ -11,6 +11,8 @@ obj-y += clk-a10-ve.o
|
||||||
obj-y += clk-a20-gmac.o
|
obj-y += clk-a20-gmac.o
|
||||||
obj-y += clk-mod0.o
|
obj-y += clk-mod0.o
|
||||||
obj-y += clk-simple-gates.o
|
obj-y += clk-simple-gates.o
|
||||||
|
obj-y += clk-sun4i-pll3.o
|
||||||
|
obj-y += clk-sun4i-tcon-ch1.o
|
||||||
obj-y += clk-sun8i-bus-gates.o
|
obj-y += clk-sun8i-bus-gates.o
|
||||||
obj-y += clk-sun8i-mbus.o
|
obj-y += clk-sun8i-mbus.o
|
||||||
obj-y += clk-sun9i-core.o
|
obj-y += clk-sun9i-core.o
|
||||||
|
|
|
@ -62,7 +62,7 @@ static void __init sun4i_mod1_clk_setup(struct device_node *node)
|
||||||
clk = clk_register_composite(NULL, clk_name, parents, i,
|
clk = clk_register_composite(NULL, clk_name, parents, i,
|
||||||
&mux->hw, &clk_mux_ops,
|
&mux->hw, &clk_mux_ops,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
&gate->hw, &clk_gate_ops, 0);
|
&gate->hw, &clk_gate_ops, CLK_SET_RATE_PARENT);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
goto err_free_gate;
|
goto err_free_gate;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Maxime Ripard
|
||||||
|
*
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define SUN4I_A10_PLL3_GATE_BIT 31
|
||||||
|
#define SUN4I_A10_PLL3_DIV_WIDTH 7
|
||||||
|
#define SUN4I_A10_PLL3_DIV_SHIFT 0
|
||||||
|
|
||||||
|
static DEFINE_SPINLOCK(sun4i_a10_pll3_lock);
|
||||||
|
|
||||||
|
static void __init sun4i_a10_pll3_setup(struct device_node *node)
|
||||||
|
{
|
||||||
|
const char *clk_name = node->name, *parent;
|
||||||
|
struct clk_multiplier *mult;
|
||||||
|
struct clk_gate *gate;
|
||||||
|
struct resource res;
|
||||||
|
void __iomem *reg;
|
||||||
|
struct clk *clk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||||
|
parent = of_clk_get_parent_name(node, 0);
|
||||||
|
|
||||||
|
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
|
||||||
|
if (IS_ERR(reg)) {
|
||||||
|
pr_err("%s: Could not map the clock registers\n", clk_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||||
|
if (!gate)
|
||||||
|
goto err_unmap;
|
||||||
|
|
||||||
|
gate->reg = reg;
|
||||||
|
gate->bit_idx = SUN4I_A10_PLL3_GATE_BIT;
|
||||||
|
gate->lock = &sun4i_a10_pll3_lock;
|
||||||
|
|
||||||
|
mult = kzalloc(sizeof(*mult), GFP_KERNEL);
|
||||||
|
if (!mult)
|
||||||
|
goto err_free_gate;
|
||||||
|
|
||||||
|
mult->reg = reg;
|
||||||
|
mult->shift = SUN4I_A10_PLL3_DIV_SHIFT;
|
||||||
|
mult->width = SUN4I_A10_PLL3_DIV_WIDTH;
|
||||||
|
mult->lock = &sun4i_a10_pll3_lock;
|
||||||
|
|
||||||
|
clk = clk_register_composite(NULL, clk_name,
|
||||||
|
&parent, 1,
|
||||||
|
NULL, NULL,
|
||||||
|
&mult->hw, &clk_multiplier_ops,
|
||||||
|
&gate->hw, &clk_gate_ops,
|
||||||
|
0);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
pr_err("%s: Couldn't register the clock\n", clk_name);
|
||||||
|
goto err_free_mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: Couldn't register DT provider\n",
|
||||||
|
clk_name);
|
||||||
|
goto err_clk_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_clk_unregister:
|
||||||
|
clk_unregister_composite(clk);
|
||||||
|
err_free_mult:
|
||||||
|
kfree(mult);
|
||||||
|
err_free_gate:
|
||||||
|
kfree(gate);
|
||||||
|
err_unmap:
|
||||||
|
iounmap(reg);
|
||||||
|
of_address_to_resource(node, 0, &res);
|
||||||
|
release_mem_region(res.start, resource_size(&res));
|
||||||
|
}
|
||||||
|
|
||||||
|
CLK_OF_DECLARE(sun4i_a10_pll3, "allwinner,sun4i-a10-pll3-clk",
|
||||||
|
sun4i_a10_pll3_setup);
|
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Maxime Ripard
|
||||||
|
*
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define TCON_CH1_SCLK2_PARENTS 4
|
||||||
|
|
||||||
|
#define TCON_CH1_SCLK2_GATE_BIT BIT(31)
|
||||||
|
#define TCON_CH1_SCLK2_MUX_MASK 3
|
||||||
|
#define TCON_CH1_SCLK2_MUX_SHIFT 24
|
||||||
|
#define TCON_CH1_SCLK2_DIV_MASK 0xf
|
||||||
|
#define TCON_CH1_SCLK2_DIV_SHIFT 0
|
||||||
|
|
||||||
|
#define TCON_CH1_SCLK1_GATE_BIT BIT(15)
|
||||||
|
#define TCON_CH1_SCLK1_HALF_BIT BIT(11)
|
||||||
|
|
||||||
|
struct tcon_ch1_clk {
|
||||||
|
struct clk_hw hw;
|
||||||
|
spinlock_t lock;
|
||||||
|
void __iomem *reg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define hw_to_tclk(hw) container_of(hw, struct tcon_ch1_clk, hw)
|
||||||
|
|
||||||
|
static void tcon_ch1_disable(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&tclk->lock, flags);
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
|
||||||
|
writel(reg, tclk->reg);
|
||||||
|
spin_unlock_irqrestore(&tclk->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcon_ch1_enable(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&tclk->lock, flags);
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT;
|
||||||
|
writel(reg, tclk->reg);
|
||||||
|
spin_unlock_irqrestore(&tclk->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcon_ch1_is_enabled(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 tcon_ch1_get_parent(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
int num_parents = clk_hw_get_num_parents(hw);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
|
||||||
|
reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;
|
||||||
|
|
||||||
|
if (reg >= num_parents)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&tclk->lock, flags);
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT);
|
||||||
|
reg |= index << TCON_CH1_SCLK2_MUX_SHIFT;
|
||||||
|
writel(reg, tclk->reg);
|
||||||
|
spin_unlock_irqrestore(&tclk->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned long tcon_ch1_calc_divider(unsigned long rate,
|
||||||
|
unsigned long parent_rate,
|
||||||
|
u8 *div,
|
||||||
|
bool *half)
|
||||||
|
{
|
||||||
|
unsigned long best_rate = 0;
|
||||||
|
u8 best_m = 0, m;
|
||||||
|
bool is_double;
|
||||||
|
|
||||||
|
for (m = 1; m < 16; m++) {
|
||||||
|
u8 d;
|
||||||
|
|
||||||
|
for (d = 1; d < 3; d++) {
|
||||||
|
unsigned long tmp_rate;
|
||||||
|
|
||||||
|
tmp_rate = parent_rate / m / d;
|
||||||
|
|
||||||
|
if (tmp_rate > rate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!best_rate ||
|
||||||
|
(rate - tmp_rate) < (rate - best_rate)) {
|
||||||
|
best_rate = tmp_rate;
|
||||||
|
best_m = m;
|
||||||
|
is_double = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (div && half) {
|
||||||
|
*div = best_m;
|
||||||
|
*half = is_double;
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcon_ch1_determine_rate(struct clk_hw *hw,
|
||||||
|
struct clk_rate_request *req)
|
||||||
|
{
|
||||||
|
long best_rate = -EINVAL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||||
|
unsigned long parent_rate;
|
||||||
|
unsigned long tmp_rate;
|
||||||
|
struct clk_hw *parent;
|
||||||
|
|
||||||
|
parent = clk_hw_get_parent_by_index(hw, i);
|
||||||
|
if (!parent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
parent_rate = clk_hw_get_rate(parent);
|
||||||
|
|
||||||
|
tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
if (best_rate < 0 ||
|
||||||
|
(req->rate - tmp_rate) < (req->rate - best_rate)) {
|
||||||
|
best_rate = tmp_rate;
|
||||||
|
req->best_parent_rate = parent_rate;
|
||||||
|
req->best_parent_hw = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_rate < 0)
|
||||||
|
return best_rate;
|
||||||
|
|
||||||
|
req->rate = best_rate;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
|
||||||
|
parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1;
|
||||||
|
|
||||||
|
if (reg & TCON_CH1_SCLK1_HALF_BIT)
|
||||||
|
parent_rate /= 2;
|
||||||
|
|
||||||
|
return parent_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
bool half;
|
||||||
|
u8 div_m;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&tclk->lock, flags);
|
||||||
|
reg = readl(tclk->reg);
|
||||||
|
reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT);
|
||||||
|
reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK;
|
||||||
|
|
||||||
|
if (half)
|
||||||
|
reg |= TCON_CH1_SCLK1_HALF_BIT;
|
||||||
|
|
||||||
|
writel(reg, tclk->reg);
|
||||||
|
spin_unlock_irqrestore(&tclk->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops tcon_ch1_ops = {
|
||||||
|
.disable = tcon_ch1_disable,
|
||||||
|
.enable = tcon_ch1_enable,
|
||||||
|
.is_enabled = tcon_ch1_is_enabled,
|
||||||
|
|
||||||
|
.get_parent = tcon_ch1_get_parent,
|
||||||
|
.set_parent = tcon_ch1_set_parent,
|
||||||
|
|
||||||
|
.determine_rate = tcon_ch1_determine_rate,
|
||||||
|
.recalc_rate = tcon_ch1_recalc_rate,
|
||||||
|
.set_rate = tcon_ch1_set_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init tcon_ch1_setup(struct device_node *node)
|
||||||
|
{
|
||||||
|
const char *parents[TCON_CH1_SCLK2_PARENTS];
|
||||||
|
const char *clk_name = node->name;
|
||||||
|
struct clk_init_data init;
|
||||||
|
struct tcon_ch1_clk *tclk;
|
||||||
|
struct resource res;
|
||||||
|
struct clk *clk;
|
||||||
|
void __iomem *reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||||
|
|
||||||
|
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
|
||||||
|
if (IS_ERR(reg)) {
|
||||||
|
pr_err("%s: Could not map the clock registers\n", clk_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS);
|
||||||
|
if (ret != TCON_CH1_SCLK2_PARENTS) {
|
||||||
|
pr_err("%s Could not retrieve the parents\n", clk_name);
|
||||||
|
goto err_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
tclk = kzalloc(sizeof(*tclk), GFP_KERNEL);
|
||||||
|
if (!tclk)
|
||||||
|
goto err_unmap;
|
||||||
|
|
||||||
|
init.name = clk_name;
|
||||||
|
init.ops = &tcon_ch1_ops;
|
||||||
|
init.parent_names = parents;
|
||||||
|
init.num_parents = TCON_CH1_SCLK2_PARENTS;
|
||||||
|
init.flags = CLK_SET_RATE_PARENT;
|
||||||
|
|
||||||
|
tclk->reg = reg;
|
||||||
|
tclk->hw.init = &init;
|
||||||
|
spin_lock_init(&tclk->lock);
|
||||||
|
|
||||||
|
clk = clk_register(NULL, &tclk->hw);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
pr_err("%s: Couldn't register the clock\n", clk_name);
|
||||||
|
goto err_free_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: Couldn't register our clock provider\n", clk_name);
|
||||||
|
goto err_unregister_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_unregister_clk:
|
||||||
|
clk_unregister(clk);
|
||||||
|
err_free_data:
|
||||||
|
kfree(tclk);
|
||||||
|
err_unmap:
|
||||||
|
iounmap(reg);
|
||||||
|
of_address_to_resource(node, 0, &res);
|
||||||
|
release_mem_region(res.start, resource_size(&res));
|
||||||
|
}
|
||||||
|
|
||||||
|
CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",
|
||||||
|
tcon_ch1_setup);
|
|
@ -106,7 +106,7 @@ static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
/* one clock/reset pair per word */
|
/* one clock/reset pair per word */
|
||||||
count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH);
|
count = DIV_ROUND_UP((resource_size(r)), SUN9I_MMC_WIDTH);
|
||||||
data->membase = devm_ioremap_resource(&pdev->dev, r);
|
data->membase = devm_ioremap_resource(&pdev->dev, r);
|
||||||
if (IS_ERR(data->membase))
|
if (IS_ERR(data->membase))
|
||||||
return PTR_ERR(data->membase);
|
return PTR_ERR(data->membase);
|
||||||
|
|
|
@ -523,21 +523,12 @@ static const struct factors_data sun4i_pll5_data __initconst = {
|
||||||
.enable = 31,
|
.enable = 31,
|
||||||
.table = &sun4i_pll5_config,
|
.table = &sun4i_pll5_config,
|
||||||
.getter = sun4i_get_pll5_factors,
|
.getter = sun4i_get_pll5_factors,
|
||||||
.name = "pll5",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct factors_data sun4i_pll6_data __initconst = {
|
|
||||||
.enable = 31,
|
|
||||||
.table = &sun4i_pll5_config,
|
|
||||||
.getter = sun4i_get_pll5_factors,
|
|
||||||
.name = "pll6",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct factors_data sun6i_a31_pll6_data __initconst = {
|
static const struct factors_data sun6i_a31_pll6_data __initconst = {
|
||||||
.enable = 31,
|
.enable = 31,
|
||||||
.table = &sun6i_a31_pll6_config,
|
.table = &sun6i_a31_pll6_config,
|
||||||
.getter = sun6i_a31_get_pll6_factors,
|
.getter = sun6i_a31_get_pll6_factors,
|
||||||
.name = "pll6x2",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct factors_data sun5i_a13_ahb_data __initconst = {
|
static const struct factors_data sun5i_a13_ahb_data __initconst = {
|
||||||
|
@ -933,7 +924,7 @@ static const struct divs_data pll5_divs_data __initconst = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct divs_data pll6_divs_data __initconst = {
|
static const struct divs_data pll6_divs_data __initconst = {
|
||||||
.factors = &sun4i_pll6_data,
|
.factors = &sun4i_pll5_data,
|
||||||
.ndivs = 4,
|
.ndivs = 4,
|
||||||
.div = {
|
.div = {
|
||||||
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
|
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
|
||||||
|
@ -975,6 +966,8 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node,
|
||||||
struct clk_gate *gate = NULL;
|
struct clk_gate *gate = NULL;
|
||||||
struct clk_fixed_factor *fix_factor;
|
struct clk_fixed_factor *fix_factor;
|
||||||
struct clk_divider *divider;
|
struct clk_divider *divider;
|
||||||
|
struct factors_data factors = *data->factors;
|
||||||
|
char *derived_name = NULL;
|
||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
int ndivs = SUNXI_DIVS_MAX_QTY, i = 0;
|
int ndivs = SUNXI_DIVS_MAX_QTY, i = 0;
|
||||||
int flags, clkflags;
|
int flags, clkflags;
|
||||||
|
@ -983,11 +976,37 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node,
|
||||||
if (data->ndivs)
|
if (data->ndivs)
|
||||||
ndivs = data->ndivs;
|
ndivs = data->ndivs;
|
||||||
|
|
||||||
|
/* Try to find a name for base factor clock */
|
||||||
|
for (i = 0; i < ndivs; i++) {
|
||||||
|
if (data->div[i].self) {
|
||||||
|
of_property_read_string_index(node, "clock-output-names",
|
||||||
|
i, &factors.name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If we don't have a .self clk use the first output-name up to '_' */
|
||||||
|
if (factors.name == NULL) {
|
||||||
|
char *endp;
|
||||||
|
|
||||||
|
of_property_read_string_index(node, "clock-output-names",
|
||||||
|
0, &clk_name);
|
||||||
|
endp = strchr(clk_name, '_');
|
||||||
|
if (endp) {
|
||||||
|
derived_name = kstrndup(clk_name, endp - clk_name,
|
||||||
|
GFP_KERNEL);
|
||||||
|
factors.name = derived_name;
|
||||||
|
} else {
|
||||||
|
factors.name = clk_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Set up factor clock that we will be dividing */
|
/* Set up factor clock that we will be dividing */
|
||||||
pclk = sunxi_factors_clk_setup(node, data->factors);
|
pclk = sunxi_factors_clk_setup(node, &factors);
|
||||||
if (!pclk)
|
if (!pclk)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
parent = __clk_get_name(pclk);
|
parent = __clk_get_name(pclk);
|
||||||
|
kfree(derived_name);
|
||||||
|
|
||||||
reg = of_iomap(node, 0);
|
reg = of_iomap(node, 0);
|
||||||
if (!reg) {
|
if (!reg) {
|
||||||
|
@ -1127,3 +1146,41 @@ static void __init sun6i_pll6_clk_setup(struct device_node *node)
|
||||||
}
|
}
|
||||||
CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk",
|
CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk",
|
||||||
sun6i_pll6_clk_setup);
|
sun6i_pll6_clk_setup);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sun6i display
|
||||||
|
*
|
||||||
|
* rate = parent_rate / (m + 1);
|
||||||
|
*/
|
||||||
|
static void sun6i_display_factors(struct factors_request *req)
|
||||||
|
{
|
||||||
|
u8 m;
|
||||||
|
|
||||||
|
if (req->rate > req->parent_rate)
|
||||||
|
req->rate = req->parent_rate;
|
||||||
|
|
||||||
|
m = DIV_ROUND_UP(req->parent_rate, req->rate);
|
||||||
|
|
||||||
|
req->rate = req->parent_rate / m;
|
||||||
|
req->m = m - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_factors_config sun6i_display_config = {
|
||||||
|
.mshift = 0,
|
||||||
|
.mwidth = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct factors_data sun6i_display_data __initconst = {
|
||||||
|
.enable = 31,
|
||||||
|
.mux = 24,
|
||||||
|
.muxmask = BIT(2) | BIT(1) | BIT(0),
|
||||||
|
.table = &sun6i_display_config,
|
||||||
|
.getter = sun6i_display_factors,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init sun6i_display_setup(struct device_node *node)
|
||||||
|
{
|
||||||
|
sunxi_factors_clk_setup(node, &sun6i_display_data);
|
||||||
|
}
|
||||||
|
CLK_OF_DECLARE(sun6i_display, "allwinner,sun6i-a31-display-clk",
|
||||||
|
sun6i_display_setup);
|
||||||
|
|
Loading…
Reference in New Issue