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:
Коммит
51de8760fb
|
@ -10,6 +10,7 @@ Required properties:
|
|||
"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,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,sun4i-a10-pll5-clk" - for the PLL5 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,sun9i-a80-apbs-gates-clk" - for the APBS gates on A80
|
||||
"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,sun4i-a10-mmc-clk" - for the MMC clock
|
||||
"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,sun7i-a20-out-clk" - for the external output clocks
|
||||
"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,sun5i-a13-usb-clk" - for usb gates + resets on A13
|
||||
"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-phy-clk" - for usb phy gates + resets on A80
|
||||
"allwinner,sun4i-a10-ve-clk" - for the Video Engine clock
|
||||
"allwinner,sun6i-a31-display-clk" - for the display clocks
|
||||
|
||||
Required properties for all clocks:
|
||||
- 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-mod0.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-mbus.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,
|
||||
&mux->hw, &clk_mux_ops,
|
||||
NULL, NULL,
|
||||
&gate->hw, &clk_gate_ops, 0);
|
||||
&gate->hw, &clk_gate_ops, CLK_SET_RATE_PARENT);
|
||||
if (IS_ERR(clk))
|
||||
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);
|
||||
/* 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);
|
||||
if (IS_ERR(data->membase))
|
||||
return PTR_ERR(data->membase);
|
||||
|
|
|
@ -523,21 +523,12 @@ static const struct factors_data sun4i_pll5_data __initconst = {
|
|||
.enable = 31,
|
||||
.table = &sun4i_pll5_config,
|
||||
.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 = {
|
||||
.enable = 31,
|
||||
.table = &sun6i_a31_pll6_config,
|
||||
.getter = sun6i_a31_get_pll6_factors,
|
||||
.name = "pll6x2",
|
||||
};
|
||||
|
||||
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 = {
|
||||
.factors = &sun4i_pll6_data,
|
||||
.factors = &sun4i_pll5_data,
|
||||
.ndivs = 4,
|
||||
.div = {
|
||||
{ .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_fixed_factor *fix_factor;
|
||||
struct clk_divider *divider;
|
||||
struct factors_data factors = *data->factors;
|
||||
char *derived_name = NULL;
|
||||
void __iomem *reg;
|
||||
int ndivs = SUNXI_DIVS_MAX_QTY, i = 0;
|
||||
int flags, clkflags;
|
||||
|
@ -983,11 +976,37 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node,
|
|||
if (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 */
|
||||
pclk = sunxi_factors_clk_setup(node, data->factors);
|
||||
pclk = sunxi_factors_clk_setup(node, &factors);
|
||||
if (!pclk)
|
||||
return NULL;
|
||||
|
||||
parent = __clk_get_name(pclk);
|
||||
kfree(derived_name);
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
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",
|
||||
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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче