mirror of https://gitee.com/openkylin/linux.git
Allwinner Clocks Additions for 3.18
The most important part of this serie is the addition of the phase API to handle the MMC clocks in the Allwinner SoCs. Apart from that, the A23 gained a new mbus driver, and there's a fix for a incorrect divider table on the APB0 clock. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUJm3zAAoJEBx+YmzsjxAgxJwQAJk3+Oq3J54jzRxKLGjUpfy9 Ma9p/78ZSnYlYWrEn62vzu7sGeMJsPo4Lsmy+Hch2r765+PzFZw9oDaxjFT5poQy Mv8F7Uyetc99sGAfmg/fKnzgQpp1t+9+kB42cV6lzjXolqX/ACcIjzFOzROXEF9B 2bnQ3RwXqvQhKKryDBg9+hJYt1R15d4SxQ7Rn6lb6WsZTxjGVO0cvvU3tp4QGQgg ZDUkJNLzLYdMK9XUNyqreatmz+HMxL5vYHeEWFz388ECp9DRUPT3MqlQcUqgSLlD eMqQPOnd5p5ZEUdB8qAAtf4kIbQTaVa7/4u37sE/+fogw6Pq/6a2Jqppl9aJWD7I PDFjxSMl77W5mQZSEanbc0a0qmqAqtZokDusP0bc0ETSZzmPVvohjW5Fa9Awyi0j PeN2bTglaFDPsHxKlQ31HF/e/almXkpiIXegeG0e/3VrGSrghFMQtqLEUXgVPu10 4PV8x7O2ib1VVAowwOb10qGv0fLGC8UCqL9zXVNlCy268ijjKMlNyK3U1sllphba fWBYgtg9+1YHONI1SewuYibAqROC7ICDXiqDkJVb6UWmO39HBcOFDb3HJ0EIj8T4 9v1clkVy1vONIqfvi1SeTekLovpROOxhxGtyXTpdx5qdlVhBjkEsNVHc5jh6BPHr o9TlBnnmIPajvF9wMN+H =ZkI9 -----END PGP SIGNATURE----- Merge tag 'sunxi-clocks-for-3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into clk-next Allwinner Clocks Additions for 3.18 The most important part of this serie is the addition of the phase API to handle the MMC clocks in the Allwinner SoCs. Apart from that, the A23 gained a new mbus driver, and there's a fix for a incorrect divider table on the APB0 clock.
This commit is contained in:
commit
4dc7ed32f3
|
@ -46,7 +46,11 @@ Required properties:
|
|||
"allwinner,sun6i-a31-apb2-div-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,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-sample-clk" - for the MMC sample clock on A10
|
||||
"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
|
||||
"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-usb-clk" - for usb gates + resets on A10 / A20
|
||||
|
|
|
@ -287,7 +287,7 @@ usb_clk: clk@01c200cc {
|
|||
|
||||
mbus_clk: clk@01c2015c {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-mod0-clk";
|
||||
compatible = "allwinner,sun5i-a13-mbus-clk";
|
||||
reg = <0x01c2015c 0x4>;
|
||||
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
|
||||
clock-output-names = "mbus";
|
||||
|
|
|
@ -285,7 +285,7 @@ usb_clk: clk@01c200cc {
|
|||
|
||||
mbus_clk: clk@01c2015c {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-mod0-clk";
|
||||
compatible = "allwinner,sun5i-a13-mbus-clk";
|
||||
reg = <0x01c2015c 0x4>;
|
||||
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
|
||||
clock-output-names = "mbus";
|
||||
|
|
|
@ -346,7 +346,7 @@ spi3_clk: clk@01c200d4 {
|
|||
|
||||
mbus_clk: clk@01c2015c {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-mod0-clk";
|
||||
compatible = "allwinner,sun5i-a13-mbus-clk";
|
||||
reg = <0x01c2015c 0x4>;
|
||||
clocks = <&osc24M>, <&pll6 2>, <&pll5 1>;
|
||||
clock-output-names = "mbus";
|
||||
|
|
|
@ -119,11 +119,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
|
|||
if (!c)
|
||||
return;
|
||||
|
||||
seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n",
|
||||
seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n",
|
||||
level * 3 + 1, "",
|
||||
30 - level * 3, c->name,
|
||||
c->enable_count, c->prepare_count, clk_get_rate(c),
|
||||
clk_get_accuracy(c));
|
||||
clk_get_accuracy(c), clk_get_phase(c));
|
||||
}
|
||||
|
||||
static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
|
||||
|
@ -145,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
|||
struct clk *c;
|
||||
struct hlist_head **lists = (struct hlist_head **)s->private;
|
||||
|
||||
seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n");
|
||||
seq_puts(s, "--------------------------------------------------------------------------------\n");
|
||||
seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n");
|
||||
seq_puts(s, "----------------------------------------------------------------------------------------\n");
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
|
@ -182,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
|
|||
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
|
||||
seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
|
||||
seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
|
||||
seq_printf(s, "\"phase\": %d", clk_get_phase(c));
|
||||
}
|
||||
|
||||
static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
|
||||
|
@ -266,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
|
|||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry,
|
||||
(u32 *)&clk->phase);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
|
||||
(u32 *)&clk->flags);
|
||||
if (!d)
|
||||
|
@ -1725,6 +1731,77 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(clk_set_parent);
|
||||
|
||||
/**
|
||||
* clk_set_phase - adjust the phase shift of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @degrees: number of degrees the signal is shifted
|
||||
*
|
||||
* Shifts the phase of a clock signal by the specified
|
||||
* degrees. Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* This function makes no distinction about the input or reference
|
||||
* signal that we adjust the clock signal phase against. For example
|
||||
* phase locked-loop clock signal generators we may shift phase with
|
||||
* respect to feedback clock signal input, but for other cases the
|
||||
* clock phase may be shifted with respect to some other, unspecified
|
||||
* signal.
|
||||
*
|
||||
* Additionally the concept of phase shift does not propagate through
|
||||
* the clock tree hierarchy, which sets it apart from clock rates and
|
||||
* clock accuracy. A parent clock phase attribute does not have an
|
||||
* impact on the phase attribute of a child clock.
|
||||
*/
|
||||
int clk_set_phase(struct clk *clk, int degrees)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!clk)
|
||||
goto out;
|
||||
|
||||
/* sanity check degrees */
|
||||
degrees %= 360;
|
||||
if (degrees < 0)
|
||||
degrees += 360;
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
if (!clk->ops->set_phase)
|
||||
goto out_unlock;
|
||||
|
||||
ret = clk->ops->set_phase(clk->hw, degrees);
|
||||
|
||||
if (!ret)
|
||||
clk->phase = degrees;
|
||||
|
||||
out_unlock:
|
||||
clk_prepare_unlock();
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_get_phase - return the phase shift of a clock signal
|
||||
* @clk: clock signal source
|
||||
*
|
||||
* Returns the phase shift of a clock node in degrees, otherwise returns
|
||||
* -EERROR.
|
||||
*/
|
||||
int clk_get_phase(struct clk *clk)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!clk)
|
||||
goto out;
|
||||
|
||||
clk_prepare_lock();
|
||||
ret = clk->phase;
|
||||
clk_prepare_unlock();
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_init - initialize the data structures in a struct clk
|
||||
* @dev: device initializing this clk, placeholder for now
|
||||
|
@ -1843,6 +1920,16 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|||
else
|
||||
clk->accuracy = 0;
|
||||
|
||||
/*
|
||||
* Set clk's phase.
|
||||
* Since a phase is by definition relative to its parent, just
|
||||
* query the current clock phase, or just assume it's in phase.
|
||||
*/
|
||||
if (clk->ops->get_phase)
|
||||
clk->phase = clk->ops->get_phase(clk->hw);
|
||||
else
|
||||
clk->phase = 0;
|
||||
|
||||
/*
|
||||
* Set clk's rate. The preferred method is to use .recalc_rate. For
|
||||
* simple clocks and lazy developers the default fallback is to use the
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
obj-y += clk-sunxi.o clk-factors.o
|
||||
obj-y += clk-a10-hosc.o
|
||||
obj-y += clk-a20-gmac.o
|
||||
obj-y += clk-mod0.o
|
||||
obj-y += clk-sun8i-mbus.o
|
||||
|
||||
obj-$(CONFIG_MFD_SUN6I_PRCM) += \
|
||||
clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \
|
||||
|
|
|
@ -9,18 +9,18 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "clk-factors.h"
|
||||
|
||||
/*
|
||||
* DOC: basic adjustable factor-based clock that cannot gate
|
||||
* DOC: basic adjustable factor-based clock
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_prepare only ensures that parents are prepared
|
||||
|
@ -32,6 +32,8 @@
|
|||
|
||||
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
|
||||
|
||||
#define FACTORS_MAX_PARENTS 5
|
||||
|
||||
#define SETMASK(len, pos) (((1U << (len)) - 1) << (pos))
|
||||
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
|
||||
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
|
||||
|
@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_factors_ops = {
|
||||
static const struct clk_ops clk_factors_ops = {
|
||||
.determine_rate = clk_factors_determine_rate,
|
||||
.recalc_rate = clk_factors_recalc_rate,
|
||||
.round_rate = clk_factors_round_rate,
|
||||
.set_rate = clk_factors_set_rate,
|
||||
};
|
||||
|
||||
struct clk * __init sunxi_factors_register(struct device_node *node,
|
||||
const struct factors_data *data,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_factors *factors;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_hw *gate_hw = NULL;
|
||||
struct clk_hw *mux_hw = NULL;
|
||||
const char *clk_name = node->name;
|
||||
const char *parents[FACTORS_MAX_PARENTS];
|
||||
void __iomem *reg;
|
||||
int i = 0;
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
/* if we have a mux, we will have >1 parents */
|
||||
while (i < FACTORS_MAX_PARENTS &&
|
||||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
/*
|
||||
* some factor clocks, such as pll5 and pll6, may have multiple
|
||||
* outputs, and have their name designated in factors_data
|
||||
*/
|
||||
if (data->name)
|
||||
clk_name = data->name;
|
||||
else
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
||||
if (!factors)
|
||||
return NULL;
|
||||
|
||||
/* set up factors properties */
|
||||
factors->reg = reg;
|
||||
factors->config = data->table;
|
||||
factors->get_factors = data->getter;
|
||||
factors->lock = lock;
|
||||
|
||||
/* Add a gate if this factor clock can be gated */
|
||||
if (data->enable) {
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
kfree(factors);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = data->enable;
|
||||
gate->lock = factors->lock;
|
||||
gate_hw = &gate->hw;
|
||||
}
|
||||
|
||||
/* Add a mux if this factor clock can be muxed */
|
||||
if (data->mux) {
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
if (!mux) {
|
||||
kfree(factors);
|
||||
kfree(gate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
mux->reg = reg;
|
||||
mux->shift = data->mux;
|
||||
mux->mask = SUNXI_FACTORS_MUX_MASK;
|
||||
mux->lock = factors->lock;
|
||||
mux_hw = &mux->hw;
|
||||
}
|
||||
|
||||
clk = clk_register_composite(NULL, clk_name,
|
||||
parents, i,
|
||||
mux_hw, &clk_mux_ops,
|
||||
&factors->hw, &clk_factors_ops,
|
||||
gate_hw, &clk_gate_ops, 0);
|
||||
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
|
||||
|
||||
#define SUNXI_FACTORS_MUX_MASK 0x3
|
||||
|
||||
struct clk_factors_config {
|
||||
u8 nshift;
|
||||
u8 nwidth;
|
||||
|
@ -18,6 +21,14 @@ struct clk_factors_config {
|
|||
u8 n_start;
|
||||
};
|
||||
|
||||
struct factors_data {
|
||||
int enable;
|
||||
int mux;
|
||||
struct clk_factors_config *table;
|
||||
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct clk_factors {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
|
@ -26,5 +37,8 @@ struct clk_factors {
|
|||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_factors_ops;
|
||||
struct clk * __init sunxi_factors_register(struct device_node *node,
|
||||
const struct factors_data *data,
|
||||
spinlock_t *lock);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright 2013 Emilio López
|
||||
*
|
||||
* Emilio López <emilio@elopez.com.ar>
|
||||
*
|
||||
* 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/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "clk-factors.h"
|
||||
|
||||
/**
|
||||
* sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
|
||||
* MOD0 rate is calculated as follows
|
||||
* rate = (parent_rate >> p) / (m + 1);
|
||||
*/
|
||||
|
||||
static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div, calcm, calcp;
|
||||
|
||||
/* These clocks can only divide, so we will never be able to achieve
|
||||
* frequencies higher than the parent frequency */
|
||||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
if (div < 16)
|
||||
calcp = 0;
|
||||
else if (div / 2 < 16)
|
||||
calcp = 1;
|
||||
else if (div / 4 < 16)
|
||||
calcp = 2;
|
||||
else
|
||||
calcp = 3;
|
||||
|
||||
calcm = DIV_ROUND_UP(div, 1 << calcp);
|
||||
|
||||
*freq = (parent_rate >> calcp) / calcm;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
*m = calcm - 1;
|
||||
*p = calcp;
|
||||
}
|
||||
|
||||
/* user manual says "n" but it's really "p" */
|
||||
static struct clk_factors_config sun4i_a10_mod0_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 4,
|
||||
.pshift = 16,
|
||||
.pwidth = 2,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_a10_mod0_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
.table = &sun4i_a10_mod0_config,
|
||||
.getter = sun4i_a10_get_mod0_factors,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(sun4i_a10_mod0_lock);
|
||||
|
||||
static void __init sun4i_a10_mod0_setup(struct device_node *node)
|
||||
{
|
||||
sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock);
|
||||
}
|
||||
CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
|
||||
|
||||
static DEFINE_SPINLOCK(sun5i_a13_mbus_lock);
|
||||
|
||||
static void __init sun5i_a13_mbus_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock);
|
||||
|
||||
/* The MBUS clocks needs to be always enabled */
|
||||
__clk_get(mbus);
|
||||
clk_prepare_enable(mbus);
|
||||
}
|
||||
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 clk_hw hw;
|
||||
void __iomem *reg;
|
||||
struct mmc_phase_data *data;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw)
|
||||
|
||||
static int mmc_get_phase(struct clk_hw *hw)
|
||||
{
|
||||
struct clk *mmc, *mmc_parent, *clk = hw->clk;
|
||||
struct mmc_phase *phase = to_mmc_phase(hw);
|
||||
unsigned int mmc_rate, mmc_parent_rate;
|
||||
u16 step, mmc_div;
|
||||
u32 value;
|
||||
u8 delay;
|
||||
|
||||
value = readl(phase->reg);
|
||||
delay = (value >> phase->data->offset) & 0x3;
|
||||
|
||||
if (!delay)
|
||||
return 180;
|
||||
|
||||
/* Get the main MMC clock */
|
||||
mmc = clk_get_parent(clk);
|
||||
if (!mmc)
|
||||
return -EINVAL;
|
||||
|
||||
/* And its rate */
|
||||
mmc_rate = clk_get_rate(mmc);
|
||||
if (!mmc_rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* Now, get the MMC parent (most likely some PLL) */
|
||||
mmc_parent = clk_get_parent(mmc);
|
||||
if (!mmc_parent)
|
||||
return -EINVAL;
|
||||
|
||||
/* And its rate */
|
||||
mmc_parent_rate = clk_get_rate(mmc_parent);
|
||||
if (!mmc_parent_rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get MMC clock divider */
|
||||
mmc_div = mmc_parent_rate / mmc_rate;
|
||||
|
||||
step = DIV_ROUND_CLOSEST(360, mmc_div);
|
||||
return delay * step;
|
||||
}
|
||||
|
||||
static int mmc_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct clk *mmc, *mmc_parent, *clk = hw->clk;
|
||||
struct mmc_phase *phase = to_mmc_phase(hw);
|
||||
unsigned int mmc_rate, mmc_parent_rate;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
u8 delay;
|
||||
|
||||
/* Get the main MMC clock */
|
||||
mmc = clk_get_parent(clk);
|
||||
if (!mmc)
|
||||
return -EINVAL;
|
||||
|
||||
/* And its rate */
|
||||
mmc_rate = clk_get_rate(mmc);
|
||||
if (!mmc_rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* Now, get the MMC parent (most likely some PLL) */
|
||||
mmc_parent = clk_get_parent(mmc);
|
||||
if (!mmc_parent)
|
||||
return -EINVAL;
|
||||
|
||||
/* And its rate */
|
||||
mmc_parent_rate = clk_get_rate(mmc_parent);
|
||||
if (!mmc_parent_rate)
|
||||
return -EINVAL;
|
||||
|
||||
if (degrees != 180) {
|
||||
u16 step, mmc_div;
|
||||
|
||||
/* Get MMC clock divider */
|
||||
mmc_div = mmc_parent_rate / mmc_rate;
|
||||
|
||||
/*
|
||||
* We can only outphase the clocks by multiple of the
|
||||
* PLL's period.
|
||||
*
|
||||
* Since the MMC clock in only a divider, and the
|
||||
* formula to get the outphasing in degrees is deg =
|
||||
* 360 * delta / period
|
||||
*
|
||||
* If we simplify this formula, we can see that the
|
||||
* only thing that we're concerned about is the number
|
||||
* of period we want to outphase our clock from, and
|
||||
* the divider set by the MMC clock.
|
||||
*/
|
||||
step = DIV_ROUND_CLOSEST(360, mmc_div);
|
||||
delay = DIV_ROUND_CLOSEST(degrees, step);
|
||||
} else {
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(phase->lock, flags);
|
||||
value = readl(phase->reg);
|
||||
value &= ~GENMASK(phase->data->offset + 3, phase->data->offset);
|
||||
value |= delay << phase->data->offset;
|
||||
writel(value, phase->reg);
|
||||
spin_unlock_irqrestore(phase->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops mmc_clk_ops = {
|
||||
.get_phase = mmc_get_phase,
|
||||
.set_phase = mmc_set_phase,
|
||||
};
|
||||
|
||||
static void __init sun4i_a10_mmc_phase_setup(struct device_node *node,
|
||||
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);
|
||||
if (!phase->reg)
|
||||
goto err_free;
|
||||
|
||||
phase->data = data;
|
||||
phase->lock = &sun4i_a10_mod0_lock;
|
||||
|
||||
if (of_property_read_string(node, "clock-output-names", &init.name))
|
||||
init.name = node->name;
|
||||
|
||||
clk = clk_register(NULL, &phase->hw);
|
||||
if (IS_ERR(clk))
|
||||
goto err_unmap;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
|
||||
return;
|
||||
|
||||
err_unmap:
|
||||
iounmap(phase->reg);
|
||||
err_free:
|
||||
kfree(phase);
|
||||
}
|
||||
|
||||
|
||||
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);
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2014 Chen-Yu Tsai
|
||||
*
|
||||
* Chen-Yu Tsai <wens@csie.org>
|
||||
*
|
||||
* 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/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "clk-factors.h"
|
||||
|
||||
/**
|
||||
* sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks
|
||||
* MBUS rate is calculated as follows
|
||||
* rate = parent_rate / (m + 1);
|
||||
*/
|
||||
|
||||
static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div;
|
||||
|
||||
/*
|
||||
* These clocks can only divide, so we will never be able to
|
||||
* achieve frequencies higher than the parent frequency
|
||||
*/
|
||||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
if (div > 8)
|
||||
div = 8;
|
||||
|
||||
*freq = parent_rate / div;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
*m = div - 1;
|
||||
}
|
||||
|
||||
static struct clk_factors_config sun8i_a23_mbus_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 3,
|
||||
};
|
||||
|
||||
static const struct factors_data sun8i_a23_mbus_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
.table = &sun8i_a23_mbus_config,
|
||||
.getter = sun8i_a23_get_mbus_factors,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
|
||||
|
||||
static void __init sun8i_a23_mbus_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
|
||||
&sun8i_a23_mbus_lock);
|
||||
|
||||
/* The MBUS clocks needs to be always enabled */
|
||||
__clk_get(mbus);
|
||||
clk_prepare_enable(mbus);
|
||||
}
|
||||
CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup);
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "clk-factors.h"
|
||||
|
||||
|
@ -319,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
|
||||
* MOD0 rate is calculated as follows
|
||||
* rate = (parent_rate >> p) / (m + 1);
|
||||
*/
|
||||
|
||||
static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div, calcm, calcp;
|
||||
|
||||
/* These clocks can only divide, so we will never be able to achieve
|
||||
* frequencies higher than the parent frequency */
|
||||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
if (div < 16)
|
||||
calcp = 0;
|
||||
else if (div / 2 < 16)
|
||||
calcp = 1;
|
||||
else if (div / 4 < 16)
|
||||
calcp = 2;
|
||||
else
|
||||
calcp = 3;
|
||||
|
||||
calcm = DIV_ROUND_UP(div, 1 << calcp);
|
||||
|
||||
*freq = (parent_rate >> calcp) / calcm;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
*m = calcm - 1;
|
||||
*p = calcp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B
|
||||
|
@ -440,16 +401,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
|
|||
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
||||
*/
|
||||
|
||||
#define SUNXI_FACTORS_MUX_MASK 0x3
|
||||
|
||||
struct factors_data {
|
||||
int enable;
|
||||
int mux;
|
||||
struct clk_factors_config *table;
|
||||
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun4i_pll1_config = {
|
||||
.nshift = 8,
|
||||
.nwidth = 5,
|
||||
|
@ -503,14 +454,6 @@ static struct clk_factors_config sun4i_apb1_config = {
|
|||
.pwidth = 2,
|
||||
};
|
||||
|
||||
/* user manual says "n" but it's really "p" */
|
||||
static struct clk_factors_config sun4i_mod0_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 4,
|
||||
.pshift = 16,
|
||||
.pwidth = 2,
|
||||
};
|
||||
|
||||
/* user manual says "n" but it's really "p" */
|
||||
static struct clk_factors_config sun7i_a20_out_config = {
|
||||
.mshift = 8,
|
||||
|
@ -568,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = {
|
|||
.getter = sun4i_get_apb1_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_mod0_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
.table = &sun4i_mod0_config,
|
||||
.getter = sun4i_get_mod0_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun7i_a20_out_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
|
@ -583,89 +519,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = {
|
|||
};
|
||||
|
||||
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
|
||||
const struct factors_data *data)
|
||||
const struct factors_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_factors *factors;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_hw *gate_hw = NULL;
|
||||
struct clk_hw *mux_hw = NULL;
|
||||
const char *clk_name = node->name;
|
||||
const char *parents[SUNXI_MAX_PARENTS];
|
||||
void __iomem *reg;
|
||||
int i = 0;
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
/* if we have a mux, we will have >1 parents */
|
||||
while (i < SUNXI_MAX_PARENTS &&
|
||||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
/*
|
||||
* some factor clocks, such as pll5 and pll6, may have multiple
|
||||
* outputs, and have their name designated in factors_data
|
||||
*/
|
||||
if (data->name)
|
||||
clk_name = data->name;
|
||||
else
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
||||
if (!factors)
|
||||
return NULL;
|
||||
|
||||
/* Add a gate if this factor clock can be gated */
|
||||
if (data->enable) {
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
kfree(factors);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = data->enable;
|
||||
gate->lock = &clk_lock;
|
||||
gate_hw = &gate->hw;
|
||||
}
|
||||
|
||||
/* Add a mux if this factor clock can be muxed */
|
||||
if (data->mux) {
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
if (!mux) {
|
||||
kfree(factors);
|
||||
kfree(gate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
mux->reg = reg;
|
||||
mux->shift = data->mux;
|
||||
mux->mask = SUNXI_FACTORS_MUX_MASK;
|
||||
mux->lock = &clk_lock;
|
||||
mux_hw = &mux->hw;
|
||||
}
|
||||
|
||||
/* set up factors properties */
|
||||
factors->reg = reg;
|
||||
factors->config = data->table;
|
||||
factors->get_factors = data->getter;
|
||||
factors->lock = &clk_lock;
|
||||
|
||||
clk = clk_register_composite(NULL, clk_name,
|
||||
parents, i,
|
||||
mux_hw, &clk_mux_ops,
|
||||
&factors->hw, &clk_factors_ops,
|
||||
gate_hw, &clk_gate_ops, 0);
|
||||
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
}
|
||||
|
||||
return clk;
|
||||
return sunxi_factors_register(node, data, &clk_lock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -762,10 +618,19 @@ static const struct div_data sun4i_ahb_data __initconst = {
|
|||
.width = 2,
|
||||
};
|
||||
|
||||
static const struct clk_div_table sun4i_apb0_table[] __initconst = {
|
||||
{ .val = 0, .div = 2 },
|
||||
{ .val = 1, .div = 2 },
|
||||
{ .val = 2, .div = 4 },
|
||||
{ .val = 3, .div = 8 },
|
||||
{ } /* sentinel */
|
||||
};
|
||||
|
||||
static const struct div_data sun4i_apb0_data __initconst = {
|
||||
.shift = 8,
|
||||
.pow = 1,
|
||||
.width = 2,
|
||||
.table = sun4i_apb0_table,
|
||||
};
|
||||
|
||||
static const struct div_data sun6i_a31_apb2_div_data __initconst = {
|
||||
|
@ -1199,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = {
|
|||
{.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
|
||||
{}
|
||||
};
|
||||
|
@ -1311,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node)
|
|||
CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
|
||||
|
||||
static const char *sun5i_critical_clocks[] __initdata = {
|
||||
"mbus",
|
||||
"pll5_ddr",
|
||||
"ahb_sdram",
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ struct clk {
|
|||
unsigned int enable_count;
|
||||
unsigned int prepare_count;
|
||||
unsigned long accuracy;
|
||||
int phase;
|
||||
struct hlist_head children;
|
||||
struct hlist_node child_node;
|
||||
struct hlist_node debug_node;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
|
||||
|
@ -129,6 +130,14 @@ struct dentry;
|
|||
* set then clock accuracy will be initialized to parent accuracy
|
||||
* or 0 (perfect clock) if clock has no parent.
|
||||
*
|
||||
* @get_phase: Queries the hardware to get the current phase of a clock.
|
||||
* Returned values are 0-359 degrees on success, negative
|
||||
* error codes on failure.
|
||||
*
|
||||
* @set_phase: Shift the phase this clock signal in degrees specified
|
||||
* by the second argument. Valid values for degrees are
|
||||
* 0-359. Return 0 on success, otherwise -EERROR.
|
||||
*
|
||||
* @init: Perform platform-specific initialization magic.
|
||||
* This is not not used by any of the basic clock types.
|
||||
* Please consider other ways of solving initialization problems
|
||||
|
@ -177,6 +186,8 @@ struct clk_ops {
|
|||
unsigned long parent_rate, u8 index);
|
||||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
int (*get_phase)(struct clk_hw *hw);
|
||||
int (*set_phase)(struct clk_hw *hw, int degrees);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
int (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
|
||||
};
|
||||
|
|
|
@ -106,6 +106,25 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
|
|||
*/
|
||||
long clk_get_accuracy(struct clk *clk);
|
||||
|
||||
/**
|
||||
* clk_set_phase - adjust the phase shift of a clock signal
|
||||
* @clk: clock signal source
|
||||
* @degrees: number of degrees the signal is shifted
|
||||
*
|
||||
* Shifts the phase of a clock signal by the specified degrees. Returns 0 on
|
||||
* success, -EERROR otherwise.
|
||||
*/
|
||||
int clk_set_phase(struct clk *clk, int degrees);
|
||||
|
||||
/**
|
||||
* clk_get_phase - return the phase shift of a clock signal
|
||||
* @clk: clock signal source
|
||||
*
|
||||
* Returns the phase shift of a clock node in degrees, otherwise returns
|
||||
* -EERROR.
|
||||
*/
|
||||
int clk_get_phase(struct clk *clk);
|
||||
|
||||
#else
|
||||
|
||||
static inline long clk_get_accuracy(struct clk *clk)
|
||||
|
@ -113,6 +132,16 @@ static inline long clk_get_accuracy(struct clk *clk)
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline long clk_set_phase(struct clk *clk, int phase)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline long clk_get_phase(struct clk *clk)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue