Skip to content

Commit

Permalink
Merge tag 'sunxi-clocks-for-3.18' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/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.
  • Loading branch information
Mike Turquette committed Sep 27, 2014
2 parents 5ad67d3 + 9c8176b commit 4dc7ed3
Show file tree
Hide file tree
Showing 14 changed files with 624 additions and 163 deletions.
4 changes: 4 additions & 0 deletions Documentation/devicetree/bindings/clock/sunxi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/sun5i-a10s.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@

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";
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/sun5i-a13.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@

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";
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/sun7i-a20.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@

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";
Expand Down
95 changes: 91 additions & 4 deletions drivers/clk/clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions drivers/clk/sunxi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
101 changes: 95 additions & 6 deletions drivers/clk/sunxi/clk-factors.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
*/

#include <linux/clk-provider.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/io.h>
#include <linux/err.h>
#include <linux/string.h>

#include <linux/delay.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
Expand All @@ -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))
Expand Down Expand Up @@ -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;
}
16 changes: 15 additions & 1 deletion drivers/clk/sunxi/clk-factors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Loading

0 comments on commit 4dc7ed3

Please sign in to comment.