Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 365286
b: refs/heads/master
c: e874a66
h: refs/heads/master
v: v3
  • Loading branch information
Emilio López authored and Mike Turquette committed Mar 27, 2013
1 parent 2578e7f commit 739c973
Show file tree
Hide file tree
Showing 9 changed files with 644 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b54891685162e09c4292cd38f067ca118604353c
refs/heads/master: e874a6697710f52fa8ab29487a99034d5d96fdcc
44 changes: 44 additions & 0 deletions trunk/Documentation/devicetree/bindings/clock/sunxi.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Device Tree Clock bindings for arch-sunxi

This binding uses the common clock binding[1].

[1] Documentation/devicetree/bindings/clock/clock-bindings.txt

Required properties:
- compatible : shall be one of the following:
"allwinner,sunxi-osc-clk" - for a gatable oscillator
"allwinner,sunxi-pll1-clk" - for the main PLL clock
"allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock
"allwinner,sunxi-axi-clk" - for the sunxi AXI clock
"allwinner,sunxi-ahb-clk" - for the sunxi AHB clock
"allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock
"allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock
"allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing

Required properties for all clocks:
- reg : shall be the control register address for the clock.
- clocks : shall be the input parent clock(s) phandle for the clock
- #clock-cells : from common clock binding; shall be set to 0.

For example:

osc24M: osc24M@01c20050 {
#clock-cells = <0>;
compatible = "allwinner,sunxi-osc-clk";
reg = <0x01c20050 0x4>;
clocks = <&osc24M_fixed>;
};

pll1: pll1@01c20000 {
#clock-cells = <0>;
compatible = "allwinner,sunxi-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
};

cpu: cpu@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sunxi-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>;
};
1 change: 1 addition & 0 deletions trunk/drivers/clk/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
Expand Down
5 changes: 5 additions & 0 deletions trunk/drivers/clk/sunxi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Makefile for sunxi specific clk
#

obj-y += clk-sunxi.o clk-factors.o
180 changes: 180 additions & 0 deletions trunk/drivers/clk/sunxi/clk-factors.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* Copyright (C) 2013 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 version 2 as
* published by the Free Software Foundation.
*
* Adjustable factor-based clock implementation
*/

#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 "clk-factors.h"

/*
* DOC: basic adjustable factor-based clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable.
* clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
* parent - fixed parent. No clk_set_parent support
*/

struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
struct clk_factors_config *config;
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
spinlock_t *lock;
};

#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)

#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))

#define FACTOR_SET(bit, len, reg, val) \
(((reg) & CLRMASK(len, bit)) | (val << (bit)))

static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u8 n = 1, k = 0, p = 0, m = 0;
u32 reg;
unsigned long rate;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;

/* Fetch the register value */
reg = readl(factors->reg);

/* Get each individual factor if applicable */
if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
n = FACTOR_GET(config->nshift, config->nwidth, reg);
if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
k = FACTOR_GET(config->kshift, config->kwidth, reg);
if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
m = FACTOR_GET(config->mshift, config->mwidth, reg);
if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
p = FACTOR_GET(config->pshift, config->pwidth, reg);

/* Calculate the rate */
rate = (parent_rate * n * (k + 1) >> p) / (m + 1);

return rate;
}

static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_factors *factors = to_clk_factors(hw);
factors->get_factors((u32 *)&rate, (u32)*parent_rate,
NULL, NULL, NULL, NULL);

return rate;
}

static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
u8 n, k, m, p;
u32 reg;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
unsigned long flags = 0;

factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);

if (factors->lock)
spin_lock_irqsave(factors->lock, flags);

/* Fetch the register value */
reg = readl(factors->reg);

/* Set up the new factors - macros do not do anything if width is 0 */
reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);

/* Apply them now */
writel(reg, factors->reg);

/* delay 500us so pll stabilizes */
__delay((rate >> 20) * 500 / 2);

if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);

return 0;
}

static const struct clk_ops clk_factors_ops = {
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
};

/**
* clk_register_factors - register a factors clock with
* the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust factors
* @config: shift and width of factors n, k, m and p
* @get_factors: function to calculate the factors for a given frequency
* @lock: shared register lock for this clock
*/
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors)(u32 *rate, u32 parent,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock)
{
struct clk_factors *factors;
struct clk *clk;
struct clk_init_data init;

/* allocate the factors */
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors) {
pr_err("%s: could not allocate factors clk\n", __func__);
return ERR_PTR(-ENOMEM);
}

init.name = name;
init.ops = &clk_factors_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);

/* struct clk_factors assignments */
factors->reg = reg;
factors->config = config;
factors->lock = lock;
factors->hw.init = &init;
factors->get_factors = get_factors;

/* register the clock */
clk = clk_register(dev, &factors->hw);

if (IS_ERR(clk))
kfree(factors);

return clk;
}
27 changes: 27 additions & 0 deletions trunk/drivers/clk/sunxi/clk-factors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef __MACH_SUNXI_CLK_FACTORS_H
#define __MACH_SUNXI_CLK_FACTORS_H

#include <linux/clk-provider.h>
#include <linux/clkdev.h>

#define SUNXI_FACTORS_NOT_APPLICABLE (0)

struct clk_factors_config {
u8 nshift;
u8 nwidth;
u8 kshift;
u8 kwidth;
u8 mshift;
u8 mwidth;
u8 pshift;
u8 pwidth;
};

struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors) (u32 *rate, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock);
#endif
Loading

0 comments on commit 739c973

Please sign in to comment.