mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
The common clk framework changes for 3.6 include a small number of core
framework improvments, platform ports and new DT bindings. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQDyNXAAoJEDqPOy9afJhJYT8QAMXV2AcB/nF3iOesjj7dZlCB qjEDd+fbISQMsvBrj9r4+rSktNWS451I46Yob4lfgfd7aVGD7iA8EK7kd11aSIx7 fjEfYuCoIBqdl0RJqXrKnXuoO/lKoX7kHdwdYj0BOZRDHoJs4CTpQaEhsTG2Ahp8 Sn7LmqMFe+hfIIL8gCn9/R0dUa8kGjFlNMN9y4hW01oPYRBNW0m6C9Af3SJlrJ0d Besk6c5Q6GHpuwJvir4kw9cghOXIzdJRFO4t9XkVBCWqQTSDhbIObOL9svLljJn/ OS4HKbvEpT1pY0P63qqzTv6o6VA3zGK+nt/oqa1T7EuZXktBS+G/eRNjAPEQx1BL bkLoM2zeSZ4zc/rWGrTk/rLGRSlzcHn1O+fy2P9RwtzhkIzN1FhxWd96FAWpFXsD VglwsB/WHQnZpi5VoQTtfqhBne6wo7BdNYpqrvYthG1OjsLgvqdK34HPESzvI+C1 k9BpLBLkUmfejJeu9Ox28hOlq92WVAqrZPOBSdcM3nyTenYG9JWM+OUytKs+Ph7a j8eSopG/Obe63BCTq5lpBJ6gOgjNK7DrmDZPwEDjSe+w/pE08/41uyNgEmzExxs8 1RQusSJAh6pJblixFYosrXtDsrU2HneJhR5aOFHKrHfqrXZ2bPoG/ut7wBr1opZF YJFNnvJ5zTbmku4HiEz8 =V8ad -----END PGP SIGNATURE----- Merge tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux Pull common clk framework changes from Michael Turquette: "This includes a small number of core framework improvments, platform ports and new DT bindings." Fix up trivial conflicts in drivers/clk/Makefile * tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux: (21 commits) clk: fix compile for OF && !COMMON_CLK clk: fix clk_get on of_clk_get_by_name return check clk: mxs: clk_register_clkdev mx28 usb clocks clk: add highbank clock support dt: add clock binding doc to primecell bindings clk: add DT fixed-clock binding support clk: add DT clock binding support ARM: integrator: convert to common clock clk: add versatile ICST307 driver ARM: integrator: put symbolic bus names on devices ARM: u300: convert to common clock clk: cache parent clocks only for muxes clk: wm831x: Add initial WM831x clock driver clk: Constify struct clk_init_data clk: Add CLK_IS_BASIC flag to identify basic clocks clk: Add support for rate table based dividers clk: Add support for power of two type dividers clk: mxs: imx28: decrease the frequency of ref_io1 for SSP2 and SSP3 clk: mxs: add clkdev lookup for pwm clk: mxs: Fix the GPMI clock name ...
This commit is contained in:
commit
9161c3b796
@ -13,11 +13,17 @@ Required properties:
|
||||
Optional properties:
|
||||
|
||||
- arm,primecell-periphid : Value to override the h/w value with
|
||||
- clocks : From common clock binding. First clock is phandle to clock for apb
|
||||
pclk. Additional clocks are optional and specific to those peripherals.
|
||||
- clock-names : From common clock binding. Shall be "apb_pclk" for first clock.
|
||||
|
||||
Example:
|
||||
|
||||
serial@fff36000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
arm,primecell-periphid = <0x00341011>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
|
||||
};
|
||||
|
||||
|
17
Documentation/devicetree/bindings/clock/calxeda.txt
Normal file
17
Documentation/devicetree/bindings/clock/calxeda.txt
Normal file
@ -0,0 +1,17 @@
|
||||
Device Tree Clock bindings for Calxeda highbank platform
|
||||
|
||||
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:
|
||||
"calxeda,hb-pll-clock" - for a PLL clock
|
||||
"calxeda,hb-a9periph-clock" - The A9 peripheral clock divided from the
|
||||
A9 clock.
|
||||
"calxeda,hb-a9bus-clock" - The A9 bus clock divided from the A9 clock.
|
||||
"calxeda,hb-emmc-clock" - Divided clock for MMC/SD controller.
|
||||
- reg : shall be the control register offset from SYSREGs base for the clock.
|
||||
- clocks : shall be the input parent clock phandle for the clock. This is
|
||||
either an oscillator or a pll output.
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
117
Documentation/devicetree/bindings/clock/clock-bindings.txt
Normal file
117
Documentation/devicetree/bindings/clock/clock-bindings.txt
Normal file
@ -0,0 +1,117 @@
|
||||
This binding is a work-in-progress, and are based on some experimental
|
||||
work by benh[1].
|
||||
|
||||
Sources of clock signal can be represented by any node in the device
|
||||
tree. Those nodes are designated as clock providers. Clock consumer
|
||||
nodes use a phandle and clock specifier pair to connect clock provider
|
||||
outputs to clock inputs. Similar to the gpio specifiers, a clock
|
||||
specifier is an array of one more more cells identifying the clock
|
||||
output on a device. The length of a clock specifier is defined by the
|
||||
value of a #clock-cells property in the clock provider node.
|
||||
|
||||
[1] http://patchwork.ozlabs.org/patch/31551/
|
||||
|
||||
==Clock providers==
|
||||
|
||||
Required properties:
|
||||
#clock-cells: Number of cells in a clock specifier; Typically 0 for nodes
|
||||
with a single clock output and 1 for nodes with multiple
|
||||
clock outputs.
|
||||
|
||||
Optional properties:
|
||||
clock-output-names: Recommended to be a list of strings of clock output signal
|
||||
names indexed by the first cell in the clock specifier.
|
||||
However, the meaning of clock-output-names is domain
|
||||
specific to the clock provider, and is only provided to
|
||||
encourage using the same meaning for the majority of clock
|
||||
providers. This format may not work for clock providers
|
||||
using a complex clock specifier format. In those cases it
|
||||
is recommended to omit this property and create a binding
|
||||
specific names property.
|
||||
|
||||
Clock consumer nodes must never directly reference
|
||||
the provider's clock-output-names property.
|
||||
|
||||
For example:
|
||||
|
||||
oscillator {
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "ckil", "ckih";
|
||||
};
|
||||
|
||||
- this node defines a device with two clock outputs, the first named
|
||||
"ckil" and the second named "ckih". Consumer nodes always reference
|
||||
clocks by index. The names should reflect the clock output signal
|
||||
names for the device.
|
||||
|
||||
==Clock consumers==
|
||||
|
||||
Required properties:
|
||||
clocks: List of phandle and clock specifier pairs, one pair
|
||||
for each clock input to the device. Note: if the
|
||||
clock provider specifies '0' for #clock-cells, then
|
||||
only the phandle portion of the pair will appear.
|
||||
|
||||
Optional properties:
|
||||
clock-names: List of clock input name strings sorted in the same
|
||||
order as the clocks property. Consumers drivers
|
||||
will use clock-names to match clock input names
|
||||
with clocks specifiers.
|
||||
clock-ranges: Empty property indicating that child nodes can inherit named
|
||||
clocks from this node. Useful for bus nodes to provide a
|
||||
clock to their children.
|
||||
|
||||
For example:
|
||||
|
||||
device {
|
||||
clocks = <&osc 1>, <&ref 0>;
|
||||
clock-names = "baud", "register";
|
||||
};
|
||||
|
||||
|
||||
This represents a device with two clock inputs, named "baud" and "register".
|
||||
The baud clock is connected to output 1 of the &osc device, and the register
|
||||
clock is connected to output 0 of the &ref.
|
||||
|
||||
==Example==
|
||||
|
||||
/* external oscillator */
|
||||
osc: oscillator {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <1>;
|
||||
clock-frequency = <32678>;
|
||||
clock-output-names = "osc";
|
||||
};
|
||||
|
||||
/* phase-locked-loop device, generates a higher frequency clock
|
||||
* from the external oscillator reference */
|
||||
pll: pll@4c000 {
|
||||
compatible = "vendor,some-pll-interface"
|
||||
#clock-cells = <1>;
|
||||
clocks = <&osc 0>;
|
||||
clock-names = "ref";
|
||||
reg = <0x4c000 0x1000>;
|
||||
clock-output-names = "pll", "pll-switched";
|
||||
};
|
||||
|
||||
/* UART, using the low frequency oscillator for the baud clock,
|
||||
* and the high frequency switched PLL output for register
|
||||
* clocking */
|
||||
uart@a000 {
|
||||
compatible = "fsl,imx-uart";
|
||||
reg = <0xa000 0x1000>;
|
||||
interrupts = <33>;
|
||||
clocks = <&osc 0>, <&pll 1>;
|
||||
clock-names = "baud", "register";
|
||||
};
|
||||
|
||||
This DT fragment defines three devices: an external oscillator to provide a
|
||||
low-frequency reference clock, a PLL device to generate a higher frequency
|
||||
clock signal, and a UART.
|
||||
|
||||
* The oscillator is fixed-frequency, and provides one clock output, named "osc".
|
||||
* The PLL is both a clock provider and a clock consumer. It uses the clock
|
||||
signal generated by the external oscillator, and provides two output signals
|
||||
("pll" and "pll-switched").
|
||||
* The UART has its baud clock connected the external oscillator and its
|
||||
register clock connected to the PLL clock (the "pll-switched" signal)
|
21
Documentation/devicetree/bindings/clock/fixed-clock.txt
Normal file
21
Documentation/devicetree/bindings/clock/fixed-clock.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Binding for simple fixed-rate clock sources.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties:
|
||||
- compatible : shall be "fixed-clock".
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clock-frequency : frequency of clock in Hz. Should be a single cell.
|
||||
|
||||
Optional properties:
|
||||
- gpios : From common gpio binding; gpio connection to clock enable pin.
|
||||
- clock-output-names : From common clock binding.
|
||||
|
||||
Example:
|
||||
clock {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <1000000000>;
|
||||
};
|
@ -7605,6 +7605,7 @@ W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices
|
||||
S: Supported
|
||||
F: Documentation/hwmon/wm83??
|
||||
F: arch/arm/mach-s3c64xx/mach-crag6410*
|
||||
F: drivers/clk/clk-wm83*.c
|
||||
F: drivers/leds/leds-wm83*.c
|
||||
F: drivers/hwmon/wm83??-hwmon.c
|
||||
F: drivers/input/misc/wm831x-on.c
|
||||
|
@ -273,8 +273,8 @@ config ARCH_INTEGRATOR
|
||||
bool "ARM Ltd. Integrator family"
|
||||
select ARM_AMBA
|
||||
select ARCH_HAS_CPUFREQ
|
||||
select CLKDEV_LOOKUP
|
||||
select HAVE_MACH_CLKDEV
|
||||
select COMMON_CLK
|
||||
select CLK_VERSATILE
|
||||
select HAVE_TCM
|
||||
select ICST
|
||||
select GENERIC_CLOCKEVENTS
|
||||
@ -336,6 +336,7 @@ config ARCH_VEXPRESS
|
||||
select ICST
|
||||
select NO_IOPORT
|
||||
select PLAT_VERSATILE
|
||||
select PLAT_VERSATILE_CLOCK
|
||||
select PLAT_VERSATILE_CLCD
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
@ -372,6 +373,7 @@ config ARCH_HIGHBANK
|
||||
select ARM_TIMER_SP804
|
||||
select CACHE_L2X0
|
||||
select CLKDEV_LOOKUP
|
||||
select COMMON_CLK
|
||||
select CPU_V7
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_ARM_SCU
|
||||
@ -929,7 +931,7 @@ config ARCH_U300
|
||||
select ARM_VIC
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select CLKDEV_LOOKUP
|
||||
select HAVE_MACH_CLKDEV
|
||||
select COMMON_CLK
|
||||
select GENERIC_GPIO
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
help
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 Calxeda, Inc.
|
||||
* Copyright 2011-2012 Calxeda, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -24,6 +24,7 @@
|
||||
compatible = "calxeda,highbank";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
clock-ranges;
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
@ -33,24 +34,32 @@
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <0>;
|
||||
next-level-cache = <&L2>;
|
||||
clocks = <&a9pll>;
|
||||
clock-names = "cpu";
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <1>;
|
||||
next-level-cache = <&L2>;
|
||||
clocks = <&a9pll>;
|
||||
clock-names = "cpu";
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <2>;
|
||||
next-level-cache = <&L2>;
|
||||
clocks = <&a9pll>;
|
||||
clock-names = "cpu";
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <3>;
|
||||
next-level-cache = <&L2>;
|
||||
clocks = <&a9pll>;
|
||||
clock-names = "cpu";
|
||||
};
|
||||
};
|
||||
|
||||
@ -75,12 +84,14 @@
|
||||
compatible = "arm,cortex-a9-twd-timer";
|
||||
reg = <0xfff10600 0x20>;
|
||||
interrupts = <1 13 0xf01>;
|
||||
clocks = <&a9periphclk>;
|
||||
};
|
||||
|
||||
watchdog@fff10620 {
|
||||
compatible = "arm,cortex-a9-twd-wdt";
|
||||
reg = <0xfff10620 0x20>;
|
||||
interrupts = <1 14 0xf01>;
|
||||
clocks = <&a9periphclk>;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@fff11000 {
|
||||
@ -116,12 +127,15 @@
|
||||
compatible = "calxeda,hb-sdhci";
|
||||
reg = <0xffe0e000 0x1000>;
|
||||
interrupts = <0 90 4>;
|
||||
clocks = <&eclk>;
|
||||
};
|
||||
|
||||
ipc@fff20000 {
|
||||
compatible = "arm,pl320", "arm,primecell";
|
||||
reg = <0xfff20000 0x1000>;
|
||||
interrupts = <0 7 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
gpioe: gpio@fff30000 {
|
||||
@ -130,6 +144,8 @@
|
||||
gpio-controller;
|
||||
reg = <0xfff30000 0x1000>;
|
||||
interrupts = <0 14 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
gpiof: gpio@fff31000 {
|
||||
@ -138,6 +154,8 @@
|
||||
gpio-controller;
|
||||
reg = <0xfff31000 0x1000>;
|
||||
interrupts = <0 15 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
gpiog: gpio@fff32000 {
|
||||
@ -146,6 +164,8 @@
|
||||
gpio-controller;
|
||||
reg = <0xfff32000 0x1000>;
|
||||
interrupts = <0 16 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
gpioh: gpio@fff33000 {
|
||||
@ -154,24 +174,32 @@
|
||||
gpio-controller;
|
||||
reg = <0xfff33000 0x1000>;
|
||||
interrupts = <0 17 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,sp804", "arm,primecell";
|
||||
reg = <0xfff34000 0x1000>;
|
||||
interrupts = <0 18 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
rtc@fff35000 {
|
||||
compatible = "arm,pl031", "arm,primecell";
|
||||
reg = <0xfff35000 0x1000>;
|
||||
interrupts = <0 19 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
serial@fff36000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0xfff36000 0x1000>;
|
||||
interrupts = <0 20 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
smic@fff3a000 {
|
||||
@ -186,12 +214,73 @@
|
||||
sregs@fff3c000 {
|
||||
compatible = "calxeda,hb-sregs";
|
||||
reg = <0xfff3c000 0x1000>;
|
||||
|
||||
clocks {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
osc: oscillator {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <33333000>;
|
||||
};
|
||||
|
||||
ddrpll: ddrpll {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-pll-clock";
|
||||
clocks = <&osc>;
|
||||
reg = <0x108>;
|
||||
};
|
||||
|
||||
a9pll: a9pll {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-pll-clock";
|
||||
clocks = <&osc>;
|
||||
reg = <0x100>;
|
||||
};
|
||||
|
||||
a9periphclk: a9periphclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-a9periph-clock";
|
||||
clocks = <&a9pll>;
|
||||
reg = <0x104>;
|
||||
};
|
||||
|
||||
a9bclk: a9bclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-a9bus-clock";
|
||||
clocks = <&a9pll>;
|
||||
reg = <0x104>;
|
||||
};
|
||||
|
||||
emmcpll: emmcpll {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-pll-clock";
|
||||
clocks = <&osc>;
|
||||
reg = <0x10C>;
|
||||
};
|
||||
|
||||
eclk: eclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "calxeda,hb-emmc-clock";
|
||||
clocks = <&emmcpll>;
|
||||
reg = <0x114>;
|
||||
};
|
||||
|
||||
pclk: pclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <150000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dma@fff3d000 {
|
||||
compatible = "arm,pl330", "arm,primecell";
|
||||
reg = <0xfff3d000 0x1000>;
|
||||
interrupts = <0 92 4>;
|
||||
clocks = <&pclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
ethernet@fff50000 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
obj-y := clock.o highbank.o system.o smc.o
|
||||
obj-y := highbank.o system.o smc.o
|
||||
|
||||
plus_sec := $(call as-instr,.arch_extension sec,+sec)
|
||||
AFLAGS_smc.o :=-Wa,-march=armv7-a$(plus_sec)
|
||||
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Calxeda, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
struct clk {
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
int clk_enable(struct clk *clk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clk_disable(struct clk *clk)
|
||||
{}
|
||||
|
||||
unsigned long clk_get_rate(struct clk *clk)
|
||||
{
|
||||
return clk->rate;
|
||||
}
|
||||
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
return clk->rate;
|
||||
}
|
||||
|
||||
int clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk eclk = { .rate = 200000000 };
|
||||
static struct clk pclk = { .rate = 150000000 };
|
||||
|
||||
static struct clk_lookup lookups[] = {
|
||||
{ .clk = &pclk, .con_id = "apb_pclk", },
|
||||
{ .clk = &pclk, .dev_id = "sp804", },
|
||||
{ .clk = &eclk, .dev_id = "ffe0e000.sdhci", },
|
||||
{ .clk = &pclk, .dev_id = "fff36000.serial", },
|
||||
};
|
||||
|
||||
void __init highbank_clocks_init(void)
|
||||
{
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
}
|
@ -105,6 +105,11 @@ static void __init highbank_init_irq(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct clk_lookup lookup = {
|
||||
.dev_id = "sp804",
|
||||
.con_id = NULL,
|
||||
};
|
||||
|
||||
static void __init highbank_timer_init(void)
|
||||
{
|
||||
int irq;
|
||||
@ -122,6 +127,8 @@ static void __init highbank_timer_init(void)
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
|
||||
highbank_clocks_init();
|
||||
lookup.clk = of_clk_get(np, 0);
|
||||
clkdev_add(&lookup);
|
||||
|
||||
sp804_clocksource_and_sched_clock_init(timer_base + 0x20, "timer1");
|
||||
sp804_clockevents_init(timer_base, irq, "timer0");
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/serial.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
@ -41,17 +40,17 @@ static struct amba_pl010_data integrator_uart_data;
|
||||
#define KMI0_IRQ { IRQ_KMIINT0 }
|
||||
#define KMI1_IRQ { IRQ_KMIINT1 }
|
||||
|
||||
static AMBA_APB_DEVICE(rtc, "mb:15", 0,
|
||||
static AMBA_APB_DEVICE(rtc, "rtc", 0,
|
||||
INTEGRATOR_RTC_BASE, INTEGRATOR_RTC_IRQ, NULL);
|
||||
|
||||
static AMBA_APB_DEVICE(uart0, "mb:16", 0,
|
||||
static AMBA_APB_DEVICE(uart0, "uart0", 0,
|
||||
INTEGRATOR_UART0_BASE, INTEGRATOR_UART0_IRQ, &integrator_uart_data);
|
||||
|
||||
static AMBA_APB_DEVICE(uart1, "mb:17", 0,
|
||||
static AMBA_APB_DEVICE(uart1, "uart1", 0,
|
||||
INTEGRATOR_UART1_BASE, INTEGRATOR_UART1_IRQ, &integrator_uart_data);
|
||||
|
||||
static AMBA_APB_DEVICE(kmi0, "mb:18", 0, KMI0_BASE, KMI0_IRQ, NULL);
|
||||
static AMBA_APB_DEVICE(kmi1, "mb:19", 0, KMI1_BASE, KMI1_IRQ, NULL);
|
||||
static AMBA_APB_DEVICE(kmi0, "kmi0", 0, KMI0_BASE, KMI0_IRQ, NULL);
|
||||
static AMBA_APB_DEVICE(kmi1, "kmi1", 0, KMI1_BASE, KMI1_IRQ, NULL);
|
||||
|
||||
static struct amba_device *amba_devs[] __initdata = {
|
||||
&rtc_device,
|
||||
@ -61,50 +60,6 @@ static struct amba_device *amba_devs[] __initdata = {
|
||||
&kmi1_device,
|
||||
};
|
||||
|
||||
/*
|
||||
* These are fixed clocks.
|
||||
*/
|
||||
static struct clk clk24mhz = {
|
||||
.rate = 24000000,
|
||||
};
|
||||
|
||||
static struct clk uartclk = {
|
||||
.rate = 14745600,
|
||||
};
|
||||
|
||||
static struct clk dummy_apb_pclk;
|
||||
|
||||
static struct clk_lookup lookups[] = {
|
||||
{ /* Bus clock */
|
||||
.con_id = "apb_pclk",
|
||||
.clk = &dummy_apb_pclk,
|
||||
}, {
|
||||
/* Integrator/AP timer frequency */
|
||||
.dev_id = "ap_timer",
|
||||
.clk = &clk24mhz,
|
||||
}, { /* UART0 */
|
||||
.dev_id = "mb:16",
|
||||
.clk = &uartclk,
|
||||
}, { /* UART1 */
|
||||
.dev_id = "mb:17",
|
||||
.clk = &uartclk,
|
||||
}, { /* KMI0 */
|
||||
.dev_id = "mb:18",
|
||||
.clk = &clk24mhz,
|
||||
}, { /* KMI1 */
|
||||
.dev_id = "mb:19",
|
||||
.clk = &clk24mhz,
|
||||
}, { /* MMCI - IntegratorCP */
|
||||
.dev_id = "mb:1c",
|
||||
.clk = &uartclk,
|
||||
}
|
||||
};
|
||||
|
||||
void __init integrator_init_early(void)
|
||||
{
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
}
|
||||
|
||||
static int __init integrator_init(void)
|
||||
{
|
||||
int i;
|
||||
|
@ -1,26 +0,0 @@
|
||||
#ifndef __ASM_MACH_CLKDEV_H
|
||||
#define __ASM_MACH_CLKDEV_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <plat/clock.h>
|
||||
|
||||
struct clk {
|
||||
unsigned long rate;
|
||||
const struct clk_ops *ops;
|
||||
struct module *owner;
|
||||
const struct icst_params *params;
|
||||
void __iomem *vcoreg;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static inline int __clk_get(struct clk *clk)
|
||||
{
|
||||
return try_module_get(clk->owner);
|
||||
}
|
||||
|
||||
static inline void __clk_put(struct clk *clk)
|
||||
{
|
||||
module_put(clk->owner);
|
||||
}
|
||||
|
||||
#endif
|
@ -33,6 +33,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_data/clk-integrator.h>
|
||||
#include <video/vga.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
@ -174,6 +175,7 @@ static void __init ap_init_irq(void)
|
||||
|
||||
fpga_irq_init(VA_IC_BASE, "SC", IRQ_PIC_START,
|
||||
-1, INTEGRATOR_SC_VALID_INT, NULL);
|
||||
integrator_clk_init(false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -440,6 +442,10 @@ static void integrator_clockevent_init(unsigned long inrate)
|
||||
0xffffU);
|
||||
}
|
||||
|
||||
void __init ap_init_early(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up timer(s).
|
||||
*/
|
||||
@ -471,7 +477,7 @@ MACHINE_START(INTEGRATOR, "ARM-Integrator")
|
||||
.reserve = integrator_reserve,
|
||||
.map_io = ap_map_io,
|
||||
.nr_irqs = NR_IRQS_INTEGRATOR_AP,
|
||||
.init_early = integrator_init_early,
|
||||
.init_early = ap_init_early,
|
||||
.init_irq = ap_init_irq,
|
||||
.handle_irq = fpga_handle_irq,
|
||||
.timer = &ap_timer,
|
||||
|
@ -21,8 +21,8 @@
|
||||
#include <linux/amba/mmci.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/platform_data/clk-integrator.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
@ -171,64 +171,9 @@ static void __init intcp_init_irq(void)
|
||||
|
||||
fpga_irq_init(INTCP_VA_SIC_BASE, "SIC", IRQ_SIC_START,
|
||||
IRQ_CP_CPPLDINT, sic_mask, NULL);
|
||||
integrator_clk_init(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clock handling
|
||||
*/
|
||||
#define CM_LOCK (__io_address(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
|
||||
#define CM_AUXOSC (__io_address(INTEGRATOR_HDR_BASE)+0x1c)
|
||||
|
||||
static const struct icst_params cp_auxvco_params = {
|
||||
.ref = 24000000,
|
||||
.vco_max = ICST525_VCO_MAX_5V,
|
||||
.vco_min = ICST525_VCO_MIN,
|
||||
.vd_min = 8,
|
||||
.vd_max = 263,
|
||||
.rd_min = 3,
|
||||
.rd_max = 65,
|
||||
.s2div = icst525_s2div,
|
||||
.idx2s = icst525_idx2s,
|
||||
};
|
||||
|
||||
static void cp_auxvco_set(struct clk *clk, struct icst_vco vco)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(clk->vcoreg) & ~0x7ffff;
|
||||
val |= vco.v | (vco.r << 9) | (vco.s << 16);
|
||||
|
||||
writel(0xa05f, CM_LOCK);
|
||||
writel(val, clk->vcoreg);
|
||||
writel(0, CM_LOCK);
|
||||
}
|
||||
|
||||
static const struct clk_ops cp_auxclk_ops = {
|
||||
.round = icst_clk_round,
|
||||
.set = icst_clk_set,
|
||||
.setvco = cp_auxvco_set,
|
||||
};
|
||||
|
||||
static struct clk cp_auxclk = {
|
||||
.ops = &cp_auxclk_ops,
|
||||
.params = &cp_auxvco_params,
|
||||
.vcoreg = CM_AUXOSC,
|
||||
};
|
||||
|
||||
static struct clk sp804_clk = {
|
||||
.rate = 1000000,
|
||||
};
|
||||
|
||||
static struct clk_lookup cp_lookups[] = {
|
||||
{ /* CLCD */
|
||||
.dev_id = "mb:c0",
|
||||
.clk = &cp_auxclk,
|
||||
}, { /* SP804 timers */
|
||||
.dev_id = "sp804",
|
||||
.clk = &sp804_clk,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Flash handling.
|
||||
*/
|
||||
@ -336,10 +281,10 @@ static struct mmci_platform_data mmc_data = {
|
||||
#define INTEGRATOR_CP_MMC_IRQS { IRQ_CP_MMCIINT0, IRQ_CP_MMCIINT1 }
|
||||
#define INTEGRATOR_CP_AACI_IRQS { IRQ_CP_AACIINT }
|
||||
|
||||
static AMBA_APB_DEVICE(mmc, "mb:1c", 0, INTEGRATOR_CP_MMC_BASE,
|
||||
static AMBA_APB_DEVICE(mmc, "mmci", 0, INTEGRATOR_CP_MMC_BASE,
|
||||
INTEGRATOR_CP_MMC_IRQS, &mmc_data);
|
||||
|
||||
static AMBA_APB_DEVICE(aaci, "mb:1d", 0, INTEGRATOR_CP_AACI_BASE,
|
||||
static AMBA_APB_DEVICE(aaci, "aaci", 0, INTEGRATOR_CP_AACI_BASE,
|
||||
INTEGRATOR_CP_AACI_IRQS, NULL);
|
||||
|
||||
|
||||
@ -393,7 +338,7 @@ static struct clcd_board clcd_data = {
|
||||
.remove = versatile_clcd_remove_dma,
|
||||
};
|
||||
|
||||
static AMBA_AHB_DEVICE(clcd, "mb:c0", 0, INTCP_PA_CLCD_BASE,
|
||||
static AMBA_AHB_DEVICE(clcd, "clcd", 0, INTCP_PA_CLCD_BASE,
|
||||
{ IRQ_CP_CLCDCINT }, &clcd_data);
|
||||
|
||||
static struct amba_device *amba_devs[] __initdata = {
|
||||
@ -406,10 +351,6 @@ static struct amba_device *amba_devs[] __initdata = {
|
||||
|
||||
static void __init intcp_init_early(void)
|
||||
{
|
||||
clkdev_add_table(cp_lookups, ARRAY_SIZE(cp_lookups));
|
||||
|
||||
integrator_init_early();
|
||||
|
||||
#ifdef CONFIG_PLAT_VERSATILE_SCHED_CLOCK
|
||||
versatile_sched_clock_init(REFCOUNTER, 24000000);
|
||||
#endif
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Makefile for the linux kernel, U300 machine.
|
||||
#
|
||||
|
||||
obj-y := core.o clock.o timer.o
|
||||
obj-y := core.o timer.o
|
||||
obj-m :=
|
||||
obj-n :=
|
||||
obj- :=
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* arch/arm/mach-u300/include/mach/clock.h
|
||||
*
|
||||
* Copyright (C) 2004 - 2005 Nokia corporation
|
||||
* Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
|
||||
* Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc
|
||||
* Copyright (C) 2007-2009 ST-Ericsson AB
|
||||
* Adopted to ST-Ericsson U300 platforms by
|
||||
* Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MACH_CLOCK_H
|
||||
#define __MACH_CLOCK_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct clk {
|
||||
struct list_head node;
|
||||
struct module *owner;
|
||||
struct device *dev;
|
||||
const char *name;
|
||||
struct clk *parent;
|
||||
|
||||
spinlock_t lock;
|
||||
unsigned long rate;
|
||||
bool reset;
|
||||
__u16 clk_val;
|
||||
__s8 usecount;
|
||||
void __iomem * res_reg;
|
||||
__u16 res_mask;
|
||||
|
||||
bool hw_ctrld;
|
||||
|
||||
void (*recalc) (struct clk *);
|
||||
int (*set_rate) (struct clk *, unsigned long);
|
||||
unsigned long (*get_rate) (struct clk *);
|
||||
unsigned long (*round_rate) (struct clk *, unsigned long);
|
||||
void (*init) (struct clk *);
|
||||
void (*enable) (struct clk *);
|
||||
void (*disable) (struct clk *);
|
||||
};
|
||||
|
||||
int u300_clock_init(void);
|
||||
|
||||
#endif
|
@ -30,6 +30,7 @@
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_data/clk-u300.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/setup.h>
|
||||
@ -44,7 +45,6 @@
|
||||
#include <mach/dma_channels.h>
|
||||
#include <mach/gpio-u300.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "spi.h"
|
||||
#include "i2c.h"
|
||||
#include "u300-gpio.h"
|
||||
@ -1658,12 +1658,20 @@ void __init u300_init_irq(void)
|
||||
int i;
|
||||
|
||||
/* initialize clocking early, we want to clock the INTCON */
|
||||
u300_clock_init();
|
||||
u300_clk_init(U300_SYSCON_VBASE);
|
||||
|
||||
/* Bootstrap EMIF and SEMI clocks */
|
||||
clk = clk_get_sys("pl172", NULL);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_prepare_enable(clk);
|
||||
clk = clk_get_sys("semi", NULL);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
/* Clock the interrupt controller */
|
||||
clk = clk_get_sys("intcon", NULL);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_enable(clk);
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
for (i = 0; i < U300_VIC_IRQS_END; i++)
|
||||
set_bit(i, (unsigned long *) &mask[0]);
|
||||
@ -1811,13 +1819,6 @@ void __init u300_init_devices(void)
|
||||
/* Check what platform we run and print some status information */
|
||||
u300_init_check_chip();
|
||||
|
||||
/* Set system to run at PLL208, max performance, a known state. */
|
||||
val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR);
|
||||
val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK;
|
||||
writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR);
|
||||
/* Wait for the PLL208 to lock if not locked in yet */
|
||||
while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) &
|
||||
U300_SYSCON_CSR_PLL208_LOCK_IND));
|
||||
/* Initialize SPI device with some board specifics */
|
||||
u300_spi_init(&pl022_device);
|
||||
|
||||
|
@ -354,7 +354,7 @@ static void __init u300_timer_init(void)
|
||||
/* Clock the interrupt controller */
|
||||
clk = clk_get_sys("apptimer", NULL);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_enable(clk);
|
||||
clk_prepare_enable(clk);
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
setup_sched_clock(u300_read_sched_clock, 32, rate);
|
||||
|
@ -34,4 +34,11 @@ config COMMON_CLK_DEBUG
|
||||
clk_flags, clk_prepare_count, clk_enable_count &
|
||||
clk_notifier_count.
|
||||
|
||||
config COMMON_CLK_WM831X
|
||||
tristate "Clock driver for WM831x/2x PMICs"
|
||||
depends on MFD_WM831X
|
||||
---help---
|
||||
Supports the clocking subsystem of the WM831x/2x series of
|
||||
PMICs from Wolfson Microlectronics.
|
||||
|
||||
endmenu
|
||||
|
@ -1,9 +1,15 @@
|
||||
|
||||
# common clock types
|
||||
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
|
||||
clk-mux.o clk-divider.o clk-fixed-factor.o
|
||||
# SoCs specific
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
|
||||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
obj-$(CONFIG_ARCH_MXS) += mxs/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_U300) += clk-u300.o
|
||||
obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/
|
||||
|
||||
# Chip specific
|
||||
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
|
||||
|
@ -30,18 +30,89 @@
|
||||
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
|
||||
|
||||
#define div_mask(d) ((1 << (d->width)) - 1)
|
||||
#define is_power_of_two(i) !(i & ~i)
|
||||
|
||||
static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
|
||||
{
|
||||
unsigned int maxdiv = 0;
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div > maxdiv)
|
||||
maxdiv = clkt->div;
|
||||
return maxdiv;
|
||||
}
|
||||
|
||||
static unsigned int _get_maxdiv(struct clk_divider *divider)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
return div_mask(divider);
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return 1 << div_mask(divider);
|
||||
if (divider->table)
|
||||
return _get_table_maxdiv(divider->table);
|
||||
return div_mask(divider) + 1;
|
||||
}
|
||||
|
||||
static unsigned int _get_table_div(const struct clk_div_table *table,
|
||||
unsigned int val)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->val == val)
|
||||
return clkt->div;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
return val;
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return 1 << val;
|
||||
if (divider->table)
|
||||
return _get_table_div(divider->table, val);
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
static unsigned int _get_table_val(const struct clk_div_table *table,
|
||||
unsigned int div)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div == div)
|
||||
return clkt->val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int _get_val(struct clk_divider *divider, u8 div)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
return div;
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return __ffs(div);
|
||||
if (divider->table)
|
||||
return _get_table_val(divider->table, div);
|
||||
return div - 1;
|
||||
}
|
||||
|
||||
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
unsigned int div;
|
||||
unsigned int div, val;
|
||||
|
||||
div = readl(divider->reg) >> divider->shift;
|
||||
div &= div_mask(divider);
|
||||
val = readl(divider->reg) >> divider->shift;
|
||||
val &= div_mask(divider);
|
||||
|
||||
if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
|
||||
div++;
|
||||
div = _get_div(divider, val);
|
||||
if (!div) {
|
||||
WARN(1, "%s: Invalid divisor for clock %s\n", __func__,
|
||||
__clk_get_name(hw->clk));
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
@ -52,6 +123,26 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
||||
*/
|
||||
#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
|
||||
|
||||
static bool _is_valid_table_div(const struct clk_div_table *table,
|
||||
unsigned int div)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div == div)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return is_power_of_two(div);
|
||||
if (divider->table)
|
||||
return _is_valid_table_div(divider->table, div);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate)
|
||||
{
|
||||
@ -62,10 +153,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
if (!rate)
|
||||
rate = 1;
|
||||
|
||||
maxdiv = (1 << divider->width);
|
||||
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
maxdiv--;
|
||||
maxdiv = _get_maxdiv(divider);
|
||||
|
||||
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
|
||||
parent_rate = *best_parent_rate;
|
||||
@ -82,6 +170,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
maxdiv = min(ULONG_MAX / rate, maxdiv);
|
||||
|
||||
for (i = 1; i <= maxdiv; i++) {
|
||||
if (!_is_valid_div(divider, i))
|
||||
continue;
|
||||
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
|
||||
MULT_ROUND_UP(rate, i));
|
||||
now = parent_rate / i;
|
||||
@ -93,9 +183,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
}
|
||||
|
||||
if (!bestdiv) {
|
||||
bestdiv = (1 << divider->width);
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
bestdiv--;
|
||||
bestdiv = _get_maxdiv(divider);
|
||||
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
|
||||
}
|
||||
|
||||
@ -115,24 +203,22 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
unsigned int div;
|
||||
unsigned int div, value;
|
||||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
|
||||
div = parent_rate / rate;
|
||||
value = _get_val(divider, div);
|
||||
|
||||
if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
|
||||
div--;
|
||||
|
||||
if (div > div_mask(divider))
|
||||
div = div_mask(divider);
|
||||
if (value > div_mask(divider))
|
||||
value = div_mask(divider);
|
||||
|
||||
if (divider->lock)
|
||||
spin_lock_irqsave(divider->lock, flags);
|
||||
|
||||
val = readl(divider->reg);
|
||||
val &= ~(div_mask(divider) << divider->shift);
|
||||
val |= div << divider->shift;
|
||||
val |= value << divider->shift;
|
||||
writel(val, divider->reg);
|
||||
|
||||
if (divider->lock)
|
||||
@ -148,6 +234,47 @@ const struct clk_ops clk_divider_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_divider_ops);
|
||||
|
||||
static struct clk *_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk_divider *div;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
/* allocate the divider */
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (!div) {
|
||||
pr_err("%s: could not allocate divider clk\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
/* struct clk_divider assignments */
|
||||
div->reg = reg;
|
||||
div->shift = shift;
|
||||
div->width = width;
|
||||
div->flags = clk_divider_flags;
|
||||
div->lock = lock;
|
||||
div->hw.init = &init;
|
||||
div->table = table;
|
||||
|
||||
/* register the clock */
|
||||
clk = clk_register(dev, &div->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(div);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_register_divider - register a divider clock with the clock framework
|
||||
* @dev: device registering this clock
|
||||
@ -165,36 +292,30 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, spinlock_t *lock)
|
||||
{
|
||||
struct clk_divider *div;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
/* allocate the divider */
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (!div) {
|
||||
pr_err("%s: could not allocate divider clk\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
/* struct clk_divider assignments */
|
||||
div->reg = reg;
|
||||
div->shift = shift;
|
||||
div->width = width;
|
||||
div->flags = clk_divider_flags;
|
||||
div->lock = lock;
|
||||
div->hw.init = &init;
|
||||
|
||||
/* register the clock */
|
||||
clk = clk_register(dev, &div->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(div);
|
||||
|
||||
return clk;
|
||||
return _register_divider(dev, name, parent_name, flags, reg, shift,
|
||||
width, clk_divider_flags, NULL, lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_register_divider_table - register a table based divider 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 divider
|
||||
* @shift: number of bits to shift the bitfield
|
||||
* @width: width of the bitfield
|
||||
* @clk_divider_flags: divider-specific flags for this clock
|
||||
* @table: array of divider/value pairs ending with a div set to 0
|
||||
* @lock: shared register lock for this clock
|
||||
*/
|
||||
struct clk *clk_register_divider_table(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
return _register_divider(dev, name, parent_name, flags, reg, shift,
|
||||
width, clk_divider_flags, table, lock);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = flags;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* DOC: basic fixed-rate clock that cannot gate
|
||||
@ -63,7 +64,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_rate_ops;
|
||||
init.flags = flags;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
@ -79,3 +80,25 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* of_fixed_clk_setup() - Setup function for simple fixed rate clock
|
||||
*/
|
||||
void __init of_fixed_clk_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
const char *clk_name = node->name;
|
||||
u32 rate;
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &rate))
|
||||
return;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
|
||||
if (clk)
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
|
||||
#endif
|
||||
|
@ -130,7 +130,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_gate_ops;
|
||||
init.flags = flags;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
|
346
drivers/clk/clk-highbank.c
Normal file
346
drivers/clk/clk-highbank.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2011-2012 Calxeda, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
extern void __iomem *sregs_base;
|
||||
|
||||
#define HB_PLL_LOCK_500 0x20000000
|
||||
#define HB_PLL_LOCK 0x10000000
|
||||
#define HB_PLL_DIVF_SHIFT 20
|
||||
#define HB_PLL_DIVF_MASK 0x0ff00000
|
||||
#define HB_PLL_DIVQ_SHIFT 16
|
||||
#define HB_PLL_DIVQ_MASK 0x00070000
|
||||
#define HB_PLL_DIVR_SHIFT 8
|
||||
#define HB_PLL_DIVR_MASK 0x00001f00
|
||||
#define HB_PLL_RANGE_SHIFT 4
|
||||
#define HB_PLL_RANGE_MASK 0x00000070
|
||||
#define HB_PLL_BYPASS 0x00000008
|
||||
#define HB_PLL_RESET 0x00000004
|
||||
#define HB_PLL_EXT_BYPASS 0x00000002
|
||||
#define HB_PLL_EXT_ENA 0x00000001
|
||||
|
||||
#define HB_PLL_VCO_MIN_FREQ 2133000000
|
||||
#define HB_PLL_MAX_FREQ HB_PLL_VCO_MIN_FREQ
|
||||
#define HB_PLL_MIN_FREQ (HB_PLL_VCO_MIN_FREQ / 64)
|
||||
|
||||
#define HB_A9_BCLK_DIV_MASK 0x00000006
|
||||
#define HB_A9_BCLK_DIV_SHIFT 1
|
||||
#define HB_A9_PCLK_DIV 0x00000001
|
||||
|
||||
struct hb_clk {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
char *parent_name;
|
||||
};
|
||||
#define to_hb_clk(p) container_of(p, struct hb_clk, hw)
|
||||
|
||||
static int clk_pll_prepare(struct clk_hw *hwclk)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
reg &= ~HB_PLL_RESET;
|
||||
writel(reg, hbclk->reg);
|
||||
|
||||
while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
|
||||
;
|
||||
while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_pll_unprepare(struct clk_hw *hwclk)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
reg |= HB_PLL_RESET;
|
||||
writel(reg, hbclk->reg);
|
||||
}
|
||||
|
||||
static int clk_pll_enable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
reg |= HB_PLL_EXT_ENA;
|
||||
writel(reg, hbclk->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_pll_disable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
reg &= ~HB_PLL_EXT_ENA;
|
||||
writel(reg, hbclk->reg);
|
||||
}
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
unsigned long divf, divq, vco_freq, reg;
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
if (reg & HB_PLL_EXT_BYPASS)
|
||||
return parent_rate;
|
||||
|
||||
divf = (reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT;
|
||||
divq = (reg & HB_PLL_DIVQ_MASK) >> HB_PLL_DIVQ_SHIFT;
|
||||
vco_freq = parent_rate * (divf + 1);
|
||||
|
||||
return vco_freq / (1 << divq);
|
||||
}
|
||||
|
||||
static void clk_pll_calc(unsigned long rate, unsigned long ref_freq,
|
||||
u32 *pdivq, u32 *pdivf)
|
||||
{
|
||||
u32 divq, divf;
|
||||
unsigned long vco_freq;
|
||||
|
||||
if (rate < HB_PLL_MIN_FREQ)
|
||||
rate = HB_PLL_MIN_FREQ;
|
||||
if (rate > HB_PLL_MAX_FREQ)
|
||||
rate = HB_PLL_MAX_FREQ;
|
||||
|
||||
for (divq = 1; divq <= 6; divq++) {
|
||||
if ((rate * (1 << divq)) >= HB_PLL_VCO_MIN_FREQ)
|
||||
break;
|
||||
}
|
||||
|
||||
vco_freq = rate * (1 << divq);
|
||||
divf = (vco_freq + (ref_freq / 2)) / ref_freq;
|
||||
divf--;
|
||||
|
||||
*pdivq = divq;
|
||||
*pdivf = divf;
|
||||
}
|
||||
|
||||
static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
u32 divq, divf;
|
||||
unsigned long ref_freq = *parent_rate;
|
||||
|
||||
clk_pll_calc(rate, ref_freq, &divq, &divf);
|
||||
|
||||
return (ref_freq * (divf + 1)) / (1 << divq);
|
||||
}
|
||||
|
||||
static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 divq, divf;
|
||||
u32 reg;
|
||||
|
||||
clk_pll_calc(rate, parent_rate, &divq, &divf);
|
||||
|
||||
reg = readl(hbclk->reg);
|
||||
if (divf != ((reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT)) {
|
||||
/* Need to re-lock PLL, so put it into bypass mode */
|
||||
reg |= HB_PLL_EXT_BYPASS;
|
||||
writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
|
||||
|
||||
writel(reg | HB_PLL_RESET, hbclk->reg);
|
||||
reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK);
|
||||
reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT);
|
||||
writel(reg | HB_PLL_RESET, hbclk->reg);
|
||||
writel(reg, hbclk->reg);
|
||||
|
||||
while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
|
||||
;
|
||||
while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
|
||||
;
|
||||
reg |= HB_PLL_EXT_ENA;
|
||||
reg &= ~HB_PLL_EXT_BYPASS;
|
||||
} else {
|
||||
reg &= ~HB_PLL_DIVQ_MASK;
|
||||
reg |= divq << HB_PLL_DIVQ_SHIFT;
|
||||
}
|
||||
writel(reg, hbclk->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_pll_ops = {
|
||||
.prepare = clk_pll_prepare,
|
||||
.unprepare = clk_pll_unprepare,
|
||||
.enable = clk_pll_enable,
|
||||
.disable = clk_pll_disable,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_round_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
};
|
||||
|
||||
static unsigned long clk_cpu_periphclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 div = (readl(hbclk->reg) & HB_A9_PCLK_DIV) ? 8 : 4;
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static const struct clk_ops a9periphclk_ops = {
|
||||
.recalc_rate = clk_cpu_periphclk_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long clk_cpu_a9bclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 div = (readl(hbclk->reg) & HB_A9_BCLK_DIV_MASK) >> HB_A9_BCLK_DIV_SHIFT;
|
||||
|
||||
return parent_rate / (div + 2);
|
||||
}
|
||||
|
||||
static const struct clk_ops a9bclk_ops = {
|
||||
.recalc_rate = clk_cpu_a9bclk_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 div;
|
||||
|
||||
div = readl(hbclk->reg) & 0x1f;
|
||||
div++;
|
||||
div *= 2;
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
u32 div;
|
||||
|
||||
div = *parent_rate / rate;
|
||||
div++;
|
||||
div &= ~0x1;
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct hb_clk *hbclk = to_hb_clk(hwclk);
|
||||
u32 div;
|
||||
|
||||
div = parent_rate / rate;
|
||||
if (div & 0x1)
|
||||
return -EINVAL;
|
||||
|
||||
writel(div >> 1, hbclk->reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops periclk_ops = {
|
||||
.recalc_rate = clk_periclk_recalc_rate,
|
||||
.round_rate = clk_periclk_round_rate,
|
||||
.set_rate = clk_periclk_set_rate,
|
||||
};
|
||||
|
||||
static __init struct clk *hb_clk_init(struct device_node *node, const struct clk_ops *ops)
|
||||
{
|
||||
u32 reg;
|
||||
struct clk *clk;
|
||||
struct hb_clk *hb_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name;
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
|
||||
rc = of_property_read_u32(node, "reg", ®);
|
||||
if (WARN_ON(rc))
|
||||
return NULL;
|
||||
|
||||
hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!hb_clk))
|
||||
return NULL;
|
||||
|
||||
hb_clk->reg = sregs_base + reg;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
hb_clk->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &hb_clk->hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(hb_clk);
|
||||
return NULL;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init hb_pll_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &clk_pll_ops);
|
||||
}
|
||||
|
||||
static void __init hb_a9periph_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &a9periphclk_ops);
|
||||
}
|
||||
|
||||
static void __init hb_a9bus_init(struct device_node *node)
|
||||
{
|
||||
struct clk *clk = hb_clk_init(node, &a9bclk_ops);
|
||||
clk_prepare_enable(clk);
|
||||
}
|
||||
|
||||
static void __init hb_emmc_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &periclk_ops);
|
||||
}
|
||||
|
||||
static const __initconst struct of_device_id clk_match[] = {
|
||||
{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
|
||||
{ .compatible = "calxeda,hb-pll-clock", .data = hb_pll_init, },
|
||||
{ .compatible = "calxeda,hb-a9periph-clock", .data = hb_a9periph_init, },
|
||||
{ .compatible = "calxeda,hb-a9bus-clock", .data = hb_a9bus_init, },
|
||||
{ .compatible = "calxeda,hb-emmc-clock", .data = hb_emmc_init, },
|
||||
{}
|
||||
};
|
||||
|
||||
void __init highbank_clocks_init(void)
|
||||
{
|
||||
of_clk_init(clk_match);
|
||||
}
|
@ -106,7 +106,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_mux_ops;
|
||||
init.flags = flags;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
|
746
drivers/clk/clk-u300.c
Normal file
746
drivers/clk/clk-u300.c
Normal file
@ -0,0 +1,746 @@
|
||||
/*
|
||||
* U300 clock implementation
|
||||
* Copyright (C) 2007-2012 ST-Ericsson AB
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <mach/syscon.h>
|
||||
|
||||
/*
|
||||
* The clocking hierarchy currently looks like this.
|
||||
* NOTE: the idea is NOT to show how the clocks are routed on the chip!
|
||||
* The ideas is to show dependencies, so a clock higher up in the
|
||||
* hierarchy has to be on in order for another clock to be on. Now,
|
||||
* both CPU and DMA can actually be on top of the hierarchy, and that
|
||||
* is not modeled currently. Instead we have the backbone AMBA bus on
|
||||
* top. This bus cannot be programmed in any way but conceptually it
|
||||
* needs to be active for the bridges and devices to transport data.
|
||||
*
|
||||
* Please be aware that a few clocks are hw controlled, which mean that
|
||||
* the hw itself can turn on/off or change the rate of the clock when
|
||||
* needed!
|
||||
*
|
||||
* AMBA bus
|
||||
* |
|
||||
* +- CPU
|
||||
* +- FSMC NANDIF NAND Flash interface
|
||||
* +- SEMI Shared Memory interface
|
||||
* +- ISP Image Signal Processor (U335 only)
|
||||
* +- CDS (U335 only)
|
||||
* +- DMA Direct Memory Access Controller
|
||||
* +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL)
|
||||
* +- APEX
|
||||
* +- VIDEO_ENC AVE2/3 Video Encoder
|
||||
* +- XGAM Graphics Accelerator Controller
|
||||
* +- AHB
|
||||
* |
|
||||
* +- ahb:0 AHB Bridge
|
||||
* | |
|
||||
* | +- ahb:1 INTCON Interrupt controller
|
||||
* | +- ahb:3 MSPRO Memory Stick Pro controller
|
||||
* | +- ahb:4 EMIF External Memory interface
|
||||
* |
|
||||
* +- fast:0 FAST bridge
|
||||
* | |
|
||||
* | +- fast:1 MMCSD MMC/SD card reader controller
|
||||
* | +- fast:2 I2S0 PCM I2S channel 0 controller
|
||||
* | +- fast:3 I2S1 PCM I2S channel 1 controller
|
||||
* | +- fast:4 I2C0 I2C channel 0 controller
|
||||
* | +- fast:5 I2C1 I2C channel 1 controller
|
||||
* | +- fast:6 SPI SPI controller
|
||||
* | +- fast:7 UART1 Secondary UART (U335 only)
|
||||
* |
|
||||
* +- slow:0 SLOW bridge
|
||||
* |
|
||||
* +- slow:1 SYSCON (not possible to control)
|
||||
* +- slow:2 WDOG Watchdog
|
||||
* +- slow:3 UART0 primary UART
|
||||
* +- slow:4 TIMER_APP Application timer - used in Linux
|
||||
* +- slow:5 KEYPAD controller
|
||||
* +- slow:6 GPIO controller
|
||||
* +- slow:7 RTC controller
|
||||
* +- slow:8 BT Bus Tracer (not used currently)
|
||||
* +- slow:9 EH Event Handler (not used currently)
|
||||
* +- slow:a TIMER_ACC Access style timer (not used currently)
|
||||
* +- slow:b PPM (U335 only, what is that?)
|
||||
*/
|
||||
|
||||
/* Global syscon virtual base */
|
||||
static void __iomem *syscon_vbase;
|
||||
|
||||
/**
|
||||
* struct clk_syscon - U300 syscon clock
|
||||
* @hw: corresponding clock hardware entry
|
||||
* @hw_ctrld: whether this clock is hardware controlled (for refcount etc)
|
||||
* and does not need any magic pokes to be enabled/disabled
|
||||
* @reset: state holder, whether this block's reset line is asserted or not
|
||||
* @res_reg: reset line enable/disable flag register
|
||||
* @res_bit: bit for resetting or taking this consumer out of reset
|
||||
* @en_reg: clock line enable/disable flag register
|
||||
* @en_bit: bit for enabling/disabling this consumer clock line
|
||||
* @clk_val: magic value to poke in the register to enable/disable
|
||||
* this one clock
|
||||
*/
|
||||
struct clk_syscon {
|
||||
struct clk_hw hw;
|
||||
bool hw_ctrld;
|
||||
bool reset;
|
||||
void __iomem *res_reg;
|
||||
u8 res_bit;
|
||||
void __iomem *en_reg;
|
||||
u8 en_bit;
|
||||
u16 clk_val;
|
||||
};
|
||||
|
||||
#define to_syscon(_hw) container_of(_hw, struct clk_syscon, hw)
|
||||
|
||||
static DEFINE_SPINLOCK(syscon_resetreg_lock);
|
||||
|
||||
/*
|
||||
* Reset control functions. We remember if a block has been
|
||||
* taken out of reset and don't remove the reset assertion again
|
||||
* and vice versa. Currently we only remove resets so the
|
||||
* enablement function is defined out.
|
||||
*/
|
||||
static void syscon_block_reset_enable(struct clk_syscon *sclk)
|
||||
{
|
||||
unsigned long iflags;
|
||||
u16 val;
|
||||
|
||||
/* Not all blocks support resetting */
|
||||
if (!sclk->res_reg)
|
||||
return;
|
||||
spin_lock_irqsave(&syscon_resetreg_lock, iflags);
|
||||
val = readw(sclk->res_reg);
|
||||
val |= BIT(sclk->res_bit);
|
||||
writew(val, sclk->res_reg);
|
||||
spin_unlock_irqrestore(&syscon_resetreg_lock, iflags);
|
||||
sclk->reset = true;
|
||||
}
|
||||
|
||||
static void syscon_block_reset_disable(struct clk_syscon *sclk)
|
||||
{
|
||||
unsigned long iflags;
|
||||
u16 val;
|
||||
|
||||
/* Not all blocks support resetting */
|
||||
if (!sclk->res_reg)
|
||||
return;
|
||||
spin_lock_irqsave(&syscon_resetreg_lock, iflags);
|
||||
val = readw(sclk->res_reg);
|
||||
val &= ~BIT(sclk->res_bit);
|
||||
writew(val, sclk->res_reg);
|
||||
spin_unlock_irqrestore(&syscon_resetreg_lock, iflags);
|
||||
sclk->reset = false;
|
||||
}
|
||||
|
||||
static int syscon_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
|
||||
/* If the block is in reset, bring it out */
|
||||
if (sclk->reset)
|
||||
syscon_block_reset_disable(sclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void syscon_clk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
|
||||
/* Please don't force the console into reset */
|
||||
if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN)
|
||||
return;
|
||||
/* When unpreparing, force block into reset */
|
||||
if (!sclk->reset)
|
||||
syscon_block_reset_enable(sclk);
|
||||
}
|
||||
|
||||
static int syscon_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
|
||||
/* Don't touch the hardware controlled clocks */
|
||||
if (sclk->hw_ctrld)
|
||||
return 0;
|
||||
/* These cannot be controlled */
|
||||
if (sclk->clk_val == 0xFFFFU)
|
||||
return 0;
|
||||
|
||||
writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void syscon_clk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
|
||||
/* Don't touch the hardware controlled clocks */
|
||||
if (sclk->hw_ctrld)
|
||||
return;
|
||||
if (sclk->clk_val == 0xFFFFU)
|
||||
return;
|
||||
/* Please don't disable the console port */
|
||||
if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN)
|
||||
return;
|
||||
|
||||
writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCDR);
|
||||
}
|
||||
|
||||
static int syscon_clk_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
u16 val;
|
||||
|
||||
/* If no enable register defined, it's always-on */
|
||||
if (!sclk->en_reg)
|
||||
return 1;
|
||||
|
||||
val = readw(sclk->en_reg);
|
||||
val &= BIT(sclk->en_bit);
|
||||
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
static u16 syscon_get_perf(void)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = readw(syscon_vbase + U300_SYSCON_CCR);
|
||||
val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK;
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
syscon_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
u16 perf = syscon_get_perf();
|
||||
|
||||
switch(sclk->clk_val) {
|
||||
case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN:
|
||||
case U300_SYSCON_SBCER_I2C0_CLK_EN:
|
||||
case U300_SYSCON_SBCER_I2C1_CLK_EN:
|
||||
case U300_SYSCON_SBCER_MMC_CLK_EN:
|
||||
case U300_SYSCON_SBCER_SPI_CLK_EN:
|
||||
/* The FAST clocks have one progression */
|
||||
switch(perf) {
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
|
||||
return 13000000;
|
||||
default:
|
||||
return parent_rate; /* 26 MHz */
|
||||
}
|
||||
case U300_SYSCON_SBCER_DMAC_CLK_EN:
|
||||
case U300_SYSCON_SBCER_NANDIF_CLK_EN:
|
||||
case U300_SYSCON_SBCER_XGAM_CLK_EN:
|
||||
/* AMBA interconnect peripherals */
|
||||
switch(perf) {
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
|
||||
return 6500000;
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
|
||||
return 26000000;
|
||||
default:
|
||||
return parent_rate; /* 52 MHz */
|
||||
}
|
||||
case U300_SYSCON_SBCER_SEMI_CLK_EN:
|
||||
case U300_SYSCON_SBCER_EMIF_CLK_EN:
|
||||
/* EMIF speeds */
|
||||
switch(perf) {
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
|
||||
return 13000000;
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
|
||||
return 52000000;
|
||||
default:
|
||||
return 104000000;
|
||||
}
|
||||
case U300_SYSCON_SBCER_CPU_CLK_EN:
|
||||
/* And the fast CPU clock */
|
||||
switch(perf) {
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
|
||||
return 13000000;
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
|
||||
return 52000000;
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH:
|
||||
return 104000000;
|
||||
default:
|
||||
return parent_rate; /* 208 MHz */
|
||||
}
|
||||
default:
|
||||
/*
|
||||
* The SLOW clocks and default just inherit the rate of
|
||||
* their parent (typically PLL13 13 MHz).
|
||||
*/
|
||||
return parent_rate;
|
||||
}
|
||||
}
|
||||
|
||||
static long
|
||||
syscon_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
|
||||
if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN)
|
||||
return *prate;
|
||||
/* We really only support setting the rate of the CPU clock */
|
||||
if (rate <= 13000000)
|
||||
return 13000000;
|
||||
if (rate <= 52000000)
|
||||
return 52000000;
|
||||
if (rate <= 104000000)
|
||||
return 104000000;
|
||||
return 208000000;
|
||||
}
|
||||
|
||||
static int syscon_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_syscon *sclk = to_syscon(hw);
|
||||
u16 val;
|
||||
|
||||
/* We only support setting the rate of the CPU clock */
|
||||
if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN)
|
||||
return -EINVAL;
|
||||
switch (rate) {
|
||||
case 13000000:
|
||||
val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER;
|
||||
break;
|
||||
case 52000000:
|
||||
val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE;
|
||||
break;
|
||||
case 104000000:
|
||||
val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH;
|
||||
break;
|
||||
case 208000000:
|
||||
val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
val |= readw(syscon_vbase + U300_SYSCON_CCR) &
|
||||
~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ;
|
||||
writew(val, syscon_vbase + U300_SYSCON_CCR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops syscon_clk_ops = {
|
||||
.prepare = syscon_clk_prepare,
|
||||
.unprepare = syscon_clk_unprepare,
|
||||
.enable = syscon_clk_enable,
|
||||
.disable = syscon_clk_disable,
|
||||
.is_enabled = syscon_clk_is_enabled,
|
||||
.recalc_rate = syscon_clk_recalc_rate,
|
||||
.round_rate = syscon_clk_round_rate,
|
||||
.set_rate = syscon_clk_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
syscon_clk_register(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
bool hw_ctrld,
|
||||
void __iomem *res_reg, u8 res_bit,
|
||||
void __iomem *en_reg, u8 en_bit,
|
||||
u16 clk_val)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_syscon *sclk;
|
||||
struct clk_init_data init;
|
||||
|
||||
sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL);
|
||||
if (!sclk) {
|
||||
pr_err("could not allocate syscon clock %s\n",
|
||||
name);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
init.name = name;
|
||||
init.ops = &syscon_clk_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
sclk->hw.init = &init;
|
||||
sclk->hw_ctrld = hw_ctrld;
|
||||
/* Assume the block is in reset at registration */
|
||||
sclk->reset = true;
|
||||
sclk->res_reg = res_reg;
|
||||
sclk->res_bit = res_bit;
|
||||
sclk->en_reg = en_reg;
|
||||
sclk->en_bit = en_bit;
|
||||
sclk->clk_val = clk_val;
|
||||
|
||||
clk = clk_register(dev, &sclk->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(sclk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct clk_mclk - U300 MCLK clock (MMC/SD clock)
|
||||
* @hw: corresponding clock hardware entry
|
||||
* @is_mspro: if this is the memory stick clock rather than MMC/SD
|
||||
*/
|
||||
struct clk_mclk {
|
||||
struct clk_hw hw;
|
||||
bool is_mspro;
|
||||
};
|
||||
|
||||
#define to_mclk(_hw) container_of(_hw, struct clk_mclk, hw)
|
||||
|
||||
static int mclk_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_mclk *mclk = to_mclk(hw);
|
||||
u16 val;
|
||||
|
||||
/* The MMC and MSPRO clocks need some special set-up */
|
||||
if (!mclk->is_mspro) {
|
||||
/* Set default MMC clock divisor to 18.9 MHz */
|
||||
writew(0x0054U, syscon_vbase + U300_SYSCON_MMF0R);
|
||||
val = readw(syscon_vbase + U300_SYSCON_MMCR);
|
||||
/* Disable the MMC feedback clock */
|
||||
val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE;
|
||||
/* Disable MSPRO frequency */
|
||||
val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE;
|
||||
writew(val, syscon_vbase + U300_SYSCON_MMCR);
|
||||
} else {
|
||||
val = readw(syscon_vbase + U300_SYSCON_MMCR);
|
||||
/* Disable the MMC feedback clock */
|
||||
val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE;
|
||||
/* Enable MSPRO frequency */
|
||||
val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE;
|
||||
writew(val, syscon_vbase + U300_SYSCON_MMCR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
mclk_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u16 perf = syscon_get_perf();
|
||||
|
||||
switch (perf) {
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER:
|
||||
/*
|
||||
* Here, the 208 MHz PLL gets shut down and the always
|
||||
* on 13 MHz PLL used for RTC etc kicks into use
|
||||
* instead.
|
||||
*/
|
||||
return 13000000;
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH:
|
||||
case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST:
|
||||
{
|
||||
/*
|
||||
* This clock is under program control. The register is
|
||||
* divided in two nybbles, bit 7-4 gives cycles-1 to count
|
||||
* high, bit 3-0 gives cycles-1 to count low. Distribute
|
||||
* these with no more than 1 cycle difference between
|
||||
* low and high and add low and high to get the actual
|
||||
* divisor. The base PLL is 208 MHz. Writing 0x00 will
|
||||
* divide by 1 and 1 so the highest frequency possible
|
||||
* is 104 MHz.
|
||||
*
|
||||
* e.g. 0x54 =>
|
||||
* f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz
|
||||
*/
|
||||
u16 val = readw(syscon_vbase + U300_SYSCON_MMF0R) &
|
||||
U300_SYSCON_MMF0R_MASK;
|
||||
switch (val) {
|
||||
case 0x0054:
|
||||
return 18900000;
|
||||
case 0x0044:
|
||||
return 20800000;
|
||||
case 0x0043:
|
||||
return 23100000;
|
||||
case 0x0033:
|
||||
return 26000000;
|
||||
case 0x0032:
|
||||
return 29700000;
|
||||
case 0x0022:
|
||||
return 34700000;
|
||||
case 0x0021:
|
||||
return 41600000;
|
||||
case 0x0011:
|
||||
return 52000000;
|
||||
case 0x0000:
|
||||
return 104000000;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
static long
|
||||
mclk_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
if (rate <= 18900000)
|
||||
return 18900000;
|
||||
if (rate <= 20800000)
|
||||
return 20800000;
|
||||
if (rate <= 23100000)
|
||||
return 23100000;
|
||||
if (rate <= 26000000)
|
||||
return 26000000;
|
||||
if (rate <= 29700000)
|
||||
return 29700000;
|
||||
if (rate <= 34700000)
|
||||
return 34700000;
|
||||
if (rate <= 41600000)
|
||||
return 41600000;
|
||||
/* Highest rate */
|
||||
return 52000000;
|
||||
}
|
||||
|
||||
static int mclk_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u16 val;
|
||||
u16 reg;
|
||||
|
||||
switch (rate) {
|
||||
case 18900000:
|
||||
val = 0x0054;
|
||||
break;
|
||||
case 20800000:
|
||||
val = 0x0044;
|
||||
break;
|
||||
case 23100000:
|
||||
val = 0x0043;
|
||||
break;
|
||||
case 26000000:
|
||||
val = 0x0033;
|
||||
break;
|
||||
case 29700000:
|
||||
val = 0x0032;
|
||||
break;
|
||||
case 34700000:
|
||||
val = 0x0022;
|
||||
break;
|
||||
case 41600000:
|
||||
val = 0x0021;
|
||||
break;
|
||||
case 52000000:
|
||||
val = 0x0011;
|
||||
break;
|
||||
case 104000000:
|
||||
val = 0x0000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readw(syscon_vbase + U300_SYSCON_MMF0R) &
|
||||
~U300_SYSCON_MMF0R_MASK;
|
||||
writew(reg | val, syscon_vbase + U300_SYSCON_MMF0R);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops mclk_ops = {
|
||||
.prepare = mclk_clk_prepare,
|
||||
.recalc_rate = mclk_clk_recalc_rate,
|
||||
.round_rate = mclk_clk_round_rate,
|
||||
.set_rate = mclk_clk_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
mclk_clk_register(struct device *dev, const char *name,
|
||||
const char *parent_name, bool is_mspro)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_mclk *mclk;
|
||||
struct clk_init_data init;
|
||||
|
||||
mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL);
|
||||
if (!mclk) {
|
||||
pr_err("could not allocate MMC/SD clock %s\n",
|
||||
name);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
init.name = "mclk";
|
||||
init.ops = &mclk_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
mclk->hw.init = &init;
|
||||
mclk->is_mspro = is_mspro;
|
||||
|
||||
clk = clk_register(dev, &mclk->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(mclk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init u300_clk_init(void __iomem *base)
|
||||
{
|
||||
u16 val;
|
||||
struct clk *clk;
|
||||
|
||||
syscon_vbase = base;
|
||||
|
||||
/* Set system to run at PLL208, max performance, a known state. */
|
||||
val = readw(syscon_vbase + U300_SYSCON_CCR);
|
||||
val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK;
|
||||
writew(val, syscon_vbase + U300_SYSCON_CCR);
|
||||
/* Wait for the PLL208 to lock if not locked in yet */
|
||||
while (!(readw(syscon_vbase + U300_SYSCON_CSR) &
|
||||
U300_SYSCON_CSR_PLL208_LOCK_IND));
|
||||
|
||||
/* Power management enable */
|
||||
val = readw(syscon_vbase + U300_SYSCON_PMCR);
|
||||
val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE;
|
||||
writew(val, syscon_vbase + U300_SYSCON_PMCR);
|
||||
|
||||
/* These are always available (RTC and PLL13) */
|
||||
clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL,
|
||||
CLK_IS_ROOT, 32768);
|
||||
/* The watchdog sits directly on the 32 kHz clock */
|
||||
clk_register_clkdev(clk, NULL, "coh901327_wdog");
|
||||
clk = clk_register_fixed_rate(NULL, "pll13", NULL,
|
||||
CLK_IS_ROOT, 13000000);
|
||||
|
||||
/* These derive from PLL208 */
|
||||
clk = clk_register_fixed_rate(NULL, "pll208", NULL,
|
||||
CLK_IS_ROOT, 208000000);
|
||||
clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208",
|
||||
0, 1, 1);
|
||||
clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208",
|
||||
0, 1, 2);
|
||||
clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208",
|
||||
0, 1, 4);
|
||||
/* The 52 MHz is divided down to 26 MHz */
|
||||
clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk",
|
||||
0, 1, 2);
|
||||
|
||||
/* Directly on the AMBA interconnect */
|
||||
clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RRR, 3,
|
||||
syscon_vbase + U300_SYSCON_CERR, 3,
|
||||
U300_SYSCON_SBCER_CPU_CLK_EN);
|
||||
clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RRR, 4,
|
||||
syscon_vbase + U300_SYSCON_CERR, 4,
|
||||
U300_SYSCON_SBCER_DMAC_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "dma");
|
||||
clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RRR, 6,
|
||||
syscon_vbase + U300_SYSCON_CERR, 6,
|
||||
U300_SYSCON_SBCER_NANDIF_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "fsmc-nand");
|
||||
clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RRR, 8,
|
||||
syscon_vbase + U300_SYSCON_CERR, 8,
|
||||
U300_SYSCON_SBCER_XGAM_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "xgam");
|
||||
clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RRR, 9,
|
||||
syscon_vbase + U300_SYSCON_CERR, 9,
|
||||
U300_SYSCON_SBCER_SEMI_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "semi");
|
||||
|
||||
/* AHB bridge clocks */
|
||||
clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RRR, 10,
|
||||
syscon_vbase + U300_SYSCON_CERR, 10,
|
||||
U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN);
|
||||
clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RRR, 12,
|
||||
syscon_vbase + U300_SYSCON_CERR, 12,
|
||||
/* Cannot be enabled, just taken out of reset */
|
||||
0xFFFFU);
|
||||
clk_register_clkdev(clk, NULL, "intcon");
|
||||
clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RRR, 5,
|
||||
syscon_vbase + U300_SYSCON_CERR, 5,
|
||||
U300_SYSCON_SBCER_EMIF_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "pl172");
|
||||
|
||||
/* FAST bridge clocks */
|
||||
clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RFR, 0,
|
||||
syscon_vbase + U300_SYSCON_CEFR, 0,
|
||||
U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN);
|
||||
clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RFR, 1,
|
||||
syscon_vbase + U300_SYSCON_CEFR, 1,
|
||||
U300_SYSCON_SBCER_I2C0_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "stu300.0");
|
||||
clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RFR, 2,
|
||||
syscon_vbase + U300_SYSCON_CEFR, 2,
|
||||
U300_SYSCON_SBCER_I2C1_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "stu300.1");
|
||||
clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RFR, 5,
|
||||
syscon_vbase + U300_SYSCON_CEFR, 5,
|
||||
U300_SYSCON_SBCER_MMC_CLK_EN);
|
||||
clk_register_clkdev(clk, "apb_pclk", "mmci");
|
||||
clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RFR, 6,
|
||||
syscon_vbase + U300_SYSCON_CEFR, 6,
|
||||
U300_SYSCON_SBCER_SPI_CLK_EN);
|
||||
/* The SPI has no external clock for the outward bus, uses the pclk */
|
||||
clk_register_clkdev(clk, NULL, "pl022");
|
||||
clk_register_clkdev(clk, "apb_pclk", "pl022");
|
||||
|
||||
/* SLOW bridge clocks */
|
||||
clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RSR, 0,
|
||||
syscon_vbase + U300_SYSCON_CESR, 0,
|
||||
U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN);
|
||||
clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RSR, 1,
|
||||
syscon_vbase + U300_SYSCON_CESR, 1,
|
||||
U300_SYSCON_SBCER_UART_CLK_EN);
|
||||
/* Same clock is used for APB and outward bus */
|
||||
clk_register_clkdev(clk, NULL, "uart0");
|
||||
clk_register_clkdev(clk, "apb_pclk", "uart0");
|
||||
clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RSR, 4,
|
||||
syscon_vbase + U300_SYSCON_CESR, 4,
|
||||
U300_SYSCON_SBCER_GPIO_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "u300-gpio");
|
||||
clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RSR, 5,
|
||||
syscon_vbase + U300_SYSCON_CESR, 6,
|
||||
U300_SYSCON_SBCER_KEYPAD_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "coh901461-keypad");
|
||||
clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true,
|
||||
syscon_vbase + U300_SYSCON_RSR, 6,
|
||||
/* No clock enable register bit */
|
||||
NULL, 0, 0xFFFFU);
|
||||
clk_register_clkdev(clk, NULL, "rtc-coh901331");
|
||||
clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RSR, 7,
|
||||
syscon_vbase + U300_SYSCON_CESR, 7,
|
||||
U300_SYSCON_SBCER_APP_TMR_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "apptimer");
|
||||
clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false,
|
||||
syscon_vbase + U300_SYSCON_RSR, 8,
|
||||
syscon_vbase + U300_SYSCON_CESR, 8,
|
||||
U300_SYSCON_SBCER_ACC_TMR_CLK_EN);
|
||||
clk_register_clkdev(clk, NULL, "timer");
|
||||
|
||||
/* Then this special MMC/SD clock */
|
||||
clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false);
|
||||
clk_register_clkdev(clk, NULL, "mmci");
|
||||
}
|
428
drivers/clk/clk-wm831x.c
Normal file
428
drivers/clk/clk-wm831x.c
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* WM831x clock control
|
||||
*
|
||||
* Copyright 2011-2 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
|
||||
struct wm831x_clk {
|
||||
struct wm831x *wm831x;
|
||||
struct clk_hw xtal_hw;
|
||||
struct clk_hw fll_hw;
|
||||
struct clk_hw clkout_hw;
|
||||
struct clk *xtal;
|
||||
struct clk *fll;
|
||||
struct clk *clkout;
|
||||
bool xtal_ena;
|
||||
};
|
||||
|
||||
static int wm831x_xtal_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
xtal_hw);
|
||||
|
||||
return clkdata->xtal_ena;
|
||||
}
|
||||
|
||||
static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
xtal_hw);
|
||||
|
||||
if (clkdata->xtal_ena)
|
||||
return 32768;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops wm831x_xtal_ops = {
|
||||
.is_enabled = wm831x_xtal_is_enabled,
|
||||
.recalc_rate = wm831x_xtal_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_init_data wm831x_xtal_init = {
|
||||
.name = "xtal",
|
||||
.ops = &wm831x_xtal_ops,
|
||||
.flags = CLK_IS_ROOT,
|
||||
};
|
||||
|
||||
static const unsigned long wm831x_fll_auto_rates[] = {
|
||||
2048000,
|
||||
11289600,
|
||||
12000000,
|
||||
12288000,
|
||||
19200000,
|
||||
22579600,
|
||||
24000000,
|
||||
24576000,
|
||||
};
|
||||
|
||||
static int wm831x_fll_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n",
|
||||
ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (ret & WM831X_FLL_ENA) != 0;
|
||||
}
|
||||
|
||||
static int wm831x_fll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2,
|
||||
WM831X_FLL_ENA, WM831X_FLL_ENA);
|
||||
if (ret != 0)
|
||||
dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
|
||||
|
||||
usleep_range(2000, 2000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wm831x_fll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0);
|
||||
if (ret != 0)
|
||||
dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret);
|
||||
}
|
||||
|
||||
static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
|
||||
ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret & WM831X_FLL_AUTO)
|
||||
return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK];
|
||||
|
||||
dev_err(wm831x->dev, "FLL only supported in AUTO mode\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *unused)
|
||||
{
|
||||
int best = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
|
||||
if (abs(wm831x_fll_auto_rates[i] - rate) <
|
||||
abs(wm831x_fll_auto_rates[best] - rate))
|
||||
best = i;
|
||||
|
||||
return wm831x_fll_auto_rates[best];
|
||||
}
|
||||
|
||||
static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
|
||||
if (wm831x_fll_auto_rates[i] == rate)
|
||||
break;
|
||||
if (i == ARRAY_SIZE(wm831x_fll_auto_rates))
|
||||
return -EINVAL;
|
||||
|
||||
if (wm831x_fll_is_enabled(hw))
|
||||
return -EPERM;
|
||||
|
||||
return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2,
|
||||
WM831X_FLL_AUTO_FREQ_MASK, i);
|
||||
}
|
||||
|
||||
static const char *wm831x_fll_parents[] = {
|
||||
"xtal",
|
||||
"clkin",
|
||||
};
|
||||
|
||||
static u8 wm831x_fll_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
fll_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
/* AUTO mode is always clocked from the crystal */
|
||||
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
|
||||
ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret & WM831X_FLL_AUTO)
|
||||
return 0;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n",
|
||||
ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ret & WM831X_FLL_CLK_SRC_MASK) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
default:
|
||||
dev_err(wm831x->dev, "Unsupported FLL clock source %d\n",
|
||||
ret & WM831X_FLL_CLK_SRC_MASK);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct clk_ops wm831x_fll_ops = {
|
||||
.is_enabled = wm831x_fll_is_enabled,
|
||||
.prepare = wm831x_fll_prepare,
|
||||
.unprepare = wm831x_fll_unprepare,
|
||||
.round_rate = wm831x_fll_round_rate,
|
||||
.recalc_rate = wm831x_fll_recalc_rate,
|
||||
.set_rate = wm831x_fll_set_rate,
|
||||
.get_parent = wm831x_fll_get_parent,
|
||||
};
|
||||
|
||||
static struct clk_init_data wm831x_fll_init = {
|
||||
.name = "fll",
|
||||
.ops = &wm831x_fll_ops,
|
||||
.parent_names = wm831x_fll_parents,
|
||||
.num_parents = ARRAY_SIZE(wm831x_fll_parents),
|
||||
.flags = CLK_SET_RATE_GATE,
|
||||
};
|
||||
|
||||
static int wm831x_clkout_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
clkout_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
|
||||
ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (ret & WM831X_CLKOUT_ENA) != 0;
|
||||
}
|
||||
|
||||
static int wm831x_clkout_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
clkout_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_unlock(wm831x);
|
||||
if (ret != 0) {
|
||||
dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
|
||||
WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA);
|
||||
if (ret != 0)
|
||||
dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret);
|
||||
|
||||
wm831x_reg_lock(wm831x);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wm831x_clkout_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
clkout_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_unlock(wm831x);
|
||||
if (ret != 0) {
|
||||
dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
|
||||
WM831X_CLKOUT_ENA, 0);
|
||||
if (ret != 0)
|
||||
dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret);
|
||||
|
||||
wm831x_reg_lock(wm831x);
|
||||
}
|
||||
|
||||
static const char *wm831x_clkout_parents[] = {
|
||||
"xtal",
|
||||
"fll",
|
||||
};
|
||||
|
||||
static u8 wm831x_clkout_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
clkout_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
int ret;
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
|
||||
ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret & WM831X_CLKOUT_SRC)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent)
|
||||
{
|
||||
struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
|
||||
clkout_hw);
|
||||
struct wm831x *wm831x = clkdata->wm831x;
|
||||
|
||||
return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
|
||||
WM831X_CLKOUT_SRC,
|
||||
parent << WM831X_CLKOUT_SRC_SHIFT);
|
||||
}
|
||||
|
||||
static const struct clk_ops wm831x_clkout_ops = {
|
||||
.is_enabled = wm831x_clkout_is_enabled,
|
||||
.prepare = wm831x_clkout_prepare,
|
||||
.unprepare = wm831x_clkout_unprepare,
|
||||
.get_parent = wm831x_clkout_get_parent,
|
||||
.set_parent = wm831x_clkout_set_parent,
|
||||
};
|
||||
|
||||
static struct clk_init_data wm831x_clkout_init = {
|
||||
.name = "clkout",
|
||||
.ops = &wm831x_clkout_ops,
|
||||
.parent_names = wm831x_clkout_parents,
|
||||
.num_parents = ARRAY_SIZE(wm831x_clkout_parents),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
};
|
||||
|
||||
static __devinit int wm831x_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct wm831x_clk *clkdata;
|
||||
int ret;
|
||||
|
||||
clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
|
||||
if (!clkdata)
|
||||
return -ENOMEM;
|
||||
|
||||
/* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
|
||||
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
|
||||
|
||||
clkdata->xtal_hw.init = &wm831x_xtal_init;
|
||||
clkdata->xtal = clk_register(&pdev->dev, &clkdata->xtal_hw);
|
||||
if (!clkdata->xtal)
|
||||
return -EINVAL;
|
||||
|
||||
clkdata->fll_hw.init = &wm831x_fll_init;
|
||||
clkdata->fll = clk_register(&pdev->dev, &clkdata->fll_hw);
|
||||
if (!clkdata->fll) {
|
||||
ret = -EINVAL;
|
||||
goto err_xtal;
|
||||
}
|
||||
|
||||
clkdata->clkout_hw.init = &wm831x_clkout_init;
|
||||
clkdata->clkout = clk_register(&pdev->dev, &clkdata->clkout_hw);
|
||||
if (!clkdata->clkout) {
|
||||
ret = -EINVAL;
|
||||
goto err_fll;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, clkdata);
|
||||
|
||||
return 0;
|
||||
|
||||
err_fll:
|
||||
clk_unregister(clkdata->fll);
|
||||
err_xtal:
|
||||
clk_unregister(clkdata->xtal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit wm831x_clk_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x_clk *clkdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
clk_unregister(clkdata->clkout);
|
||||
clk_unregister(clkdata->fll);
|
||||
clk_unregister(clkdata->xtal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver wm831x_clk_driver = {
|
||||
.probe = wm831x_clk_probe,
|
||||
.remove = __devexit_p(wm831x_clk_remove),
|
||||
.driver = {
|
||||
.name = "wm831x-clk",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(wm831x_clk_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_DESCRIPTION("WM831x clock driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:wm831x-clk");
|
@ -16,6 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static DEFINE_SPINLOCK(enable_lock);
|
||||
static DEFINE_MUTEX(prepare_lock);
|
||||
@ -1235,8 +1236,8 @@ int __clk_init(struct device *dev, struct clk *clk)
|
||||
* If clk->parents is not NULL we skip this entire block. This allows
|
||||
* for clock drivers to statically initialize clk->parents.
|
||||
*/
|
||||
if (clk->num_parents && !clk->parents) {
|
||||
clk->parents = kmalloc((sizeof(struct clk*) * clk->num_parents),
|
||||
if (clk->num_parents > 1 && !clk->parents) {
|
||||
clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
|
||||
GFP_KERNEL);
|
||||
/*
|
||||
* __clk_lookup returns NULL for parents that have not been
|
||||
@ -1550,3 +1551,142 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_notifier_unregister);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* struct of_clk_provider - Clock provider registration structure
|
||||
* @link: Entry in global list of clock providers
|
||||
* @node: Pointer to device tree node of clock provider
|
||||
* @get: Get clock callback. Returns NULL or a struct clk for the
|
||||
* given clock specifier
|
||||
* @data: context pointer to be passed into @get callback
|
||||
*/
|
||||
struct of_clk_provider {
|
||||
struct list_head link;
|
||||
|
||||
struct device_node *node;
|
||||
struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static LIST_HEAD(of_clk_providers);
|
||||
static DEFINE_MUTEX(of_clk_lock);
|
||||
|
||||
struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
|
||||
|
||||
/**
|
||||
* of_clk_add_provider() - Register a clock provider for a node
|
||||
* @np: Device node pointer associated with clock provider
|
||||
* @clk_src_get: callback for decoding clock
|
||||
* @data: context pointer for @clk_src_get callback.
|
||||
*/
|
||||
int of_clk_add_provider(struct device_node *np,
|
||||
struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
|
||||
void *data),
|
||||
void *data)
|
||||
{
|
||||
struct of_clk_provider *cp;
|
||||
|
||||
cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL);
|
||||
if (!cp)
|
||||
return -ENOMEM;
|
||||
|
||||
cp->node = of_node_get(np);
|
||||
cp->data = data;
|
||||
cp->get = clk_src_get;
|
||||
|
||||
mutex_lock(&of_clk_lock);
|
||||
list_add(&cp->link, &of_clk_providers);
|
||||
mutex_unlock(&of_clk_lock);
|
||||
pr_debug("Added clock from %s\n", np->full_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_add_provider);
|
||||
|
||||
/**
|
||||
* of_clk_del_provider() - Remove a previously registered clock provider
|
||||
* @np: Device node pointer associated with clock provider
|
||||
*/
|
||||
void of_clk_del_provider(struct device_node *np)
|
||||
{
|
||||
struct of_clk_provider *cp;
|
||||
|
||||
mutex_lock(&of_clk_lock);
|
||||
list_for_each_entry(cp, &of_clk_providers, link) {
|
||||
if (cp->node == np) {
|
||||
list_del(&cp->link);
|
||||
of_node_put(cp->node);
|
||||
kfree(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_clk_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_del_provider);
|
||||
|
||||
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
||||
{
|
||||
struct of_clk_provider *provider;
|
||||
struct clk *clk = ERR_PTR(-ENOENT);
|
||||
|
||||
/* Check if we have such a provider in our array */
|
||||
mutex_lock(&of_clk_lock);
|
||||
list_for_each_entry(provider, &of_clk_providers, link) {
|
||||
if (provider->node == clkspec->np)
|
||||
clk = provider->get(clkspec, provider->data);
|
||||
if (!IS_ERR(clk))
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&of_clk_lock);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
const char *of_clk_get_parent_name(struct device_node *np, int index)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
const char *clk_name;
|
||||
int rc;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
|
||||
&clkspec);
|
||||
if (rc)
|
||||
return NULL;
|
||||
|
||||
if (of_property_read_string_index(clkspec.np, "clock-output-names",
|
||||
clkspec.args_count ? clkspec.args[0] : 0,
|
||||
&clk_name) < 0)
|
||||
clk_name = clkspec.np->name;
|
||||
|
||||
of_node_put(clkspec.np);
|
||||
return clk_name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
|
||||
|
||||
/**
|
||||
* of_clk_init() - Scan and init clock providers from the DT
|
||||
* @matches: array of compatible values and init functions for providers.
|
||||
*
|
||||
* This function scans the device tree for matching clock providers and
|
||||
* calls their initialization functions
|
||||
*/
|
||||
void __init of_clk_init(const struct of_device_id *matches)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
for_each_matching_node(np, matches) {
|
||||
const struct of_device_id *match = of_match_node(matches, np);
|
||||
of_clk_init_cb_t clk_init_cb = match->data;
|
||||
clk_init_cb(np);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -19,10 +19,80 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static LIST_HEAD(clocks);
|
||||
static DEFINE_MUTEX(clocks_mutex);
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
|
||||
struct clk *of_clk_get(struct device_node *np, int index)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
struct clk *clk;
|
||||
int rc;
|
||||
|
||||
if (index < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
|
||||
&clkspec);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
clk = of_clk_get_from_provider(&clkspec);
|
||||
of_node_put(clkspec.np);
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL(of_clk_get);
|
||||
|
||||
/**
|
||||
* of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
|
||||
* @np: pointer to clock consumer node
|
||||
* @name: name of consumer's clock input, or NULL for the first clock reference
|
||||
*
|
||||
* This function parses the clocks and clock-names properties,
|
||||
* and uses them to look up the struct clk from the registered list of clock
|
||||
* providers.
|
||||
*/
|
||||
struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
|
||||
{
|
||||
struct clk *clk = ERR_PTR(-ENOENT);
|
||||
|
||||
/* Walk up the tree of devices looking for a clock that matches */
|
||||
while (np) {
|
||||
int index = 0;
|
||||
|
||||
/*
|
||||
* For named clocks, first look up the name in the
|
||||
* "clock-names" property. If it cannot be found, then
|
||||
* index will be an error code, and of_clk_get() will fail.
|
||||
*/
|
||||
if (name)
|
||||
index = of_property_match_string(np, "clock-names", name);
|
||||
clk = of_clk_get(np, index);
|
||||
if (!IS_ERR(clk))
|
||||
break;
|
||||
else if (name && index >= 0) {
|
||||
pr_err("ERROR: could not get clock %s:%s(%i)\n",
|
||||
np->full_name, name ? name : "", index);
|
||||
return clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* No matching clock found on this node. If the parent node
|
||||
* has a "clock-ranges" property, then we can try one of its
|
||||
* clocks.
|
||||
*/
|
||||
np = np->parent;
|
||||
if (np && !of_get_property(np, "clock-ranges", NULL))
|
||||
break;
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL(of_clk_get_by_name);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find the correct struct clk for the device and connection ID.
|
||||
* We do slightly fuzzy matching here:
|
||||
@ -83,6 +153,13 @@ EXPORT_SYMBOL(clk_get_sys);
|
||||
struct clk *clk_get(struct device *dev, const char *con_id)
|
||||
{
|
||||
const char *dev_id = dev ? dev_name(dev) : NULL;
|
||||
struct clk *clk;
|
||||
|
||||
if (dev) {
|
||||
clk = of_clk_get_by_name(dev->of_node, con_id);
|
||||
if (!IS_ERR(clk) && __clk_get(clk))
|
||||
return clk;
|
||||
}
|
||||
|
||||
return clk_get_sys(dev_id, con_id);
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ static struct clk_lookup lcdif_lookups[] = {
|
||||
|
||||
static struct clk_lookup gpmi_lookups[] = {
|
||||
{ .dev_id = "imx23-gpmi-nand", },
|
||||
{ .dev_id = "8000c000.gpmi", },
|
||||
{ .dev_id = "8000c000.gpmi-nand", },
|
||||
};
|
||||
|
||||
static const char *sel_pll[] __initconst = { "pll", "ref_xtal", };
|
||||
@ -189,6 +189,7 @@ int __init mx23_clocks_init(void)
|
||||
}
|
||||
|
||||
clk_register_clkdev(clks[clk32k], NULL, "timrot");
|
||||
clk_register_clkdev(clks[pwm], NULL, "80064000.pwm");
|
||||
clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups));
|
||||
clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups));
|
||||
clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups));
|
||||
|
@ -112,11 +112,11 @@ static void __init clk_misc_init(void)
|
||||
|
||||
/*
|
||||
* 480 MHz seems too high to be ssp clock source directly,
|
||||
* so set frac0 to get a 288 MHz ref_io0.
|
||||
* so set frac0 to get a 288 MHz ref_io0 and ref_io1.
|
||||
*/
|
||||
val = readl_relaxed(FRAC0);
|
||||
val &= ~(0x3f << BP_FRAC0_IO0FRAC);
|
||||
val |= 30 << BP_FRAC0_IO0FRAC;
|
||||
val &= ~((0x3f << BP_FRAC0_IO0FRAC) | (0x3f << BP_FRAC0_IO1FRAC));
|
||||
val |= (30 << BP_FRAC0_IO0FRAC) | (30 << BP_FRAC0_IO1FRAC);
|
||||
writel_relaxed(val, FRAC0);
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ static struct clk_lookup lcdif_lookups[] = {
|
||||
|
||||
static struct clk_lookup gpmi_lookups[] = {
|
||||
{ .dev_id = "imx28-gpmi-nand", },
|
||||
{ .dev_id = "8000c000.gpmi", },
|
||||
{ .dev_id = "8000c000.gpmi-nand", },
|
||||
};
|
||||
|
||||
static struct clk_lookup fec_lookups[] = {
|
||||
@ -314,6 +314,7 @@ int __init mx28_clocks_init(void)
|
||||
|
||||
clk_register_clkdev(clks[clk32k], NULL, "timrot");
|
||||
clk_register_clkdev(clks[enet_out], NULL, "enet_out");
|
||||
clk_register_clkdev(clks[pwm], NULL, "80064000.pwm");
|
||||
clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups));
|
||||
clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups));
|
||||
clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups));
|
||||
@ -328,6 +329,10 @@ int __init mx28_clocks_init(void)
|
||||
clk_register_clkdevs(clks[fec], fec_lookups, ARRAY_SIZE(fec_lookups));
|
||||
clk_register_clkdevs(clks[can0], can0_lookups, ARRAY_SIZE(can0_lookups));
|
||||
clk_register_clkdevs(clks[can1], can1_lookups, ARRAY_SIZE(can1_lookups));
|
||||
clk_register_clkdev(clks[usb0_pwr], NULL, "8007c000.usbphy");
|
||||
clk_register_clkdev(clks[usb1_pwr], NULL, "8007e000.usbphy");
|
||||
clk_register_clkdev(clks[usb0], NULL, "80080000.usb");
|
||||
clk_register_clkdev(clks[usb1], NULL, "80090000.usb");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
|
||||
clk_prepare_enable(clks[clks_init_on[i]]);
|
||||
|
3
drivers/clk/versatile/Makefile
Normal file
3
drivers/clk/versatile/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# Makefile for Versatile-specific clocks
|
||||
obj-$(CONFIG_ICST) += clk-icst.o
|
||||
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
|
100
drivers/clk/versatile/clk-icst.c
Normal file
100
drivers/clk/versatile/clk-icst.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Driver for the ICST307 VCO clock found in the ARM Reference designs.
|
||||
* We wrap the custom interface from <asm/hardware/icst.h> into the generic
|
||||
* clock framework.
|
||||
*
|
||||
* TODO: when all ARM reference designs are migrated to generic clocks, the
|
||||
* ICST clock code from the ARM tree should probably be merged into this
|
||||
* file.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "clk-icst.h"
|
||||
|
||||
/**
|
||||
* struct clk_icst - ICST VCO clock wrapper
|
||||
* @hw: corresponding clock hardware entry
|
||||
* @params: parameters for this ICST instance
|
||||
* @rate: current rate
|
||||
* @setvco: function to commit ICST settings to hardware
|
||||
*/
|
||||
struct clk_icst {
|
||||
struct clk_hw hw;
|
||||
const struct icst_params *params;
|
||||
unsigned long rate;
|
||||
struct icst_vco (*getvco)(void);
|
||||
void (*setvco)(struct icst_vco);
|
||||
};
|
||||
|
||||
#define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
|
||||
|
||||
static unsigned long icst_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_icst *icst = to_icst(hw);
|
||||
struct icst_vco vco;
|
||||
|
||||
vco = icst->getvco();
|
||||
icst->rate = icst_hz(icst->params, vco);
|
||||
return icst->rate;
|
||||
}
|
||||
|
||||
static long icst_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_icst *icst = to_icst(hw);
|
||||
struct icst_vco vco;
|
||||
|
||||
vco = icst_hz_to_vco(icst->params, rate);
|
||||
return icst_hz(icst->params, vco);
|
||||
}
|
||||
|
||||
static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_icst *icst = to_icst(hw);
|
||||
struct icst_vco vco;
|
||||
|
||||
vco = icst_hz_to_vco(icst->params, rate);
|
||||
icst->rate = icst_hz(icst->params, vco);
|
||||
icst->setvco(vco);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops icst_ops = {
|
||||
.recalc_rate = icst_recalc_rate,
|
||||
.round_rate = icst_round_rate,
|
||||
.set_rate = icst_set_rate,
|
||||
};
|
||||
|
||||
struct clk * __init icst_clk_register(struct device *dev,
|
||||
const struct clk_icst_desc *desc)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_icst *icst;
|
||||
struct clk_init_data init;
|
||||
|
||||
icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL);
|
||||
if (!icst) {
|
||||
pr_err("could not allocate ICST clock!\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
init.name = "icst";
|
||||
init.ops = &icst_ops;
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
icst->hw.init = &init;
|
||||
icst->params = desc->params;
|
||||
icst->getvco = desc->getvco;
|
||||
icst->setvco = desc->setvco;
|
||||
|
||||
clk = clk_register(dev, &icst->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(icst);
|
||||
|
||||
return clk;
|
||||
}
|
10
drivers/clk/versatile/clk-icst.h
Normal file
10
drivers/clk/versatile/clk-icst.h
Normal file
@ -0,0 +1,10 @@
|
||||
#include <asm/hardware/icst.h>
|
||||
|
||||
struct clk_icst_desc {
|
||||
const struct icst_params *params;
|
||||
struct icst_vco (*getvco)(void);
|
||||
void (*setvco)(struct icst_vco);
|
||||
};
|
||||
|
||||
struct clk *icst_clk_register(struct device *dev,
|
||||
const struct clk_icst_desc *desc);
|
111
drivers/clk/versatile/clk-integrator.c
Normal file
111
drivers/clk/versatile/clk-integrator.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
|
||||
#include "clk-icst.h"
|
||||
|
||||
/*
|
||||
* Implementation of the ARM Integrator/AP and Integrator/CP clock tree.
|
||||
* Inspired by portions of:
|
||||
* plat-versatile/clock.c and plat-versatile/include/plat/clock.h
|
||||
*/
|
||||
#define CM_LOCK (__io_address(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
|
||||
#define CM_AUXOSC (__io_address(INTEGRATOR_HDR_BASE)+0x1c)
|
||||
|
||||
/**
|
||||
* cp_auxvco_get() - get ICST VCO settings for the Integrator/CP
|
||||
* @vco: ICST VCO parameters to update with hardware status
|
||||
*/
|
||||
static struct icst_vco cp_auxvco_get(void)
|
||||
{
|
||||
u32 val;
|
||||
struct icst_vco vco;
|
||||
|
||||
val = readl(CM_AUXOSC);
|
||||
vco.v = val & 0x1ff;
|
||||
vco.r = (val >> 9) & 0x7f;
|
||||
vco.s = (val >> 16) & 03;
|
||||
return vco;
|
||||
}
|
||||
|
||||
/**
|
||||
* cp_auxvco_set() - commit changes to Integrator/CP ICST VCO
|
||||
* @vco: ICST VCO parameters to commit
|
||||
*/
|
||||
static void cp_auxvco_set(struct icst_vco vco)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(CM_AUXOSC) & ~0x7ffff;
|
||||
val |= vco.v | (vco.r << 9) | (vco.s << 16);
|
||||
|
||||
/* This magic unlocks the CM VCO so it can be controlled */
|
||||
writel(0xa05f, CM_LOCK);
|
||||
writel(val, CM_AUXOSC);
|
||||
/* This locks the CM again */
|
||||
writel(0, CM_LOCK);
|
||||
}
|
||||
|
||||
static const struct icst_params cp_auxvco_params = {
|
||||
.ref = 24000000,
|
||||
.vco_max = ICST525_VCO_MAX_5V,
|
||||
.vco_min = ICST525_VCO_MIN,
|
||||
.vd_min = 8,
|
||||
.vd_max = 263,
|
||||
.rd_min = 3,
|
||||
.rd_max = 65,
|
||||
.s2div = icst525_s2div,
|
||||
.idx2s = icst525_idx2s,
|
||||
};
|
||||
|
||||
static const struct clk_icst_desc __initdata cp_icst_desc = {
|
||||
.params = &cp_auxvco_params,
|
||||
.getvco = cp_auxvco_get,
|
||||
.setvco = cp_auxvco_set,
|
||||
};
|
||||
|
||||
/*
|
||||
* integrator_clk_init() - set up the integrator clock tree
|
||||
* @is_cp: pass true if it's the Integrator/CP else AP is assumed
|
||||
*/
|
||||
void __init integrator_clk_init(bool is_cp)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
/* APB clock dummy */
|
||||
clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
|
||||
clk_register_clkdev(clk, "apb_pclk", NULL);
|
||||
|
||||
/* UART reference clock */
|
||||
clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT,
|
||||
14745600);
|
||||
clk_register_clkdev(clk, NULL, "uart0");
|
||||
clk_register_clkdev(clk, NULL, "uart1");
|
||||
if (is_cp)
|
||||
clk_register_clkdev(clk, NULL, "mmci");
|
||||
|
||||
/* 24 MHz clock */
|
||||
clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
|
||||
24000000);
|
||||
clk_register_clkdev(clk, NULL, "kmi0");
|
||||
clk_register_clkdev(clk, NULL, "kmi1");
|
||||
if (!is_cp)
|
||||
clk_register_clkdev(clk, NULL, "ap_timer");
|
||||
|
||||
if (!is_cp)
|
||||
return;
|
||||
|
||||
/* 1 MHz clock */
|
||||
clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
|
||||
1000000);
|
||||
clk_register_clkdev(clk, NULL, "sp804");
|
||||
|
||||
/* ICST VCO clock used on the Integrator/CP CLCD */
|
||||
clk = icst_clk_register(NULL, &cp_icst_desc);
|
||||
clk_register_clkdev(clk, NULL, "clcd");
|
||||
}
|
@ -64,7 +64,7 @@ struct clk {
|
||||
.parent_names = _parent_names, \
|
||||
.num_parents = ARRAY_SIZE(_parent_names), \
|
||||
.parents = _parents, \
|
||||
.flags = _flags, \
|
||||
.flags = _flags | CLK_IS_BASIC, \
|
||||
}
|
||||
|
||||
#define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate, \
|
||||
@ -103,9 +103,9 @@ struct clk {
|
||||
DEFINE_CLK(_name, clk_gate_ops, _flags, \
|
||||
_name##_parent_names, _name##_parents);
|
||||
|
||||
#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
|
||||
#define _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
|
||||
_flags, _reg, _shift, _width, \
|
||||
_divider_flags, _lock) \
|
||||
_divider_flags, _table, _lock) \
|
||||
static struct clk _name; \
|
||||
static const char *_name##_parent_names[] = { \
|
||||
_parent_name, \
|
||||
@ -121,11 +121,27 @@ struct clk {
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.flags = _divider_flags, \
|
||||
.table = _table, \
|
||||
.lock = _lock, \
|
||||
}; \
|
||||
DEFINE_CLK(_name, clk_divider_ops, _flags, \
|
||||
_name##_parent_names, _name##_parents);
|
||||
|
||||
#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
|
||||
_flags, _reg, _shift, _width, \
|
||||
_divider_flags, _lock) \
|
||||
_DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
|
||||
_flags, _reg, _shift, _width, \
|
||||
_divider_flags, NULL, _lock)
|
||||
|
||||
#define DEFINE_CLK_DIVIDER_TABLE(_name, _parent_name, \
|
||||
_parent_ptr, _flags, _reg, \
|
||||
_shift, _width, _divider_flags, \
|
||||
_table, _lock) \
|
||||
_DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
|
||||
_flags, _reg, _shift, _width, \
|
||||
_divider_flags, _table, _lock) \
|
||||
|
||||
#define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \
|
||||
_reg, _shift, _width, \
|
||||
_mux_flags, _lock) \
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */
|
||||
#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
|
||||
#define CLK_IS_ROOT BIT(4) /* root clk, has no parent */
|
||||
#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */
|
||||
|
||||
struct clk_hw;
|
||||
|
||||
@ -143,7 +144,7 @@ struct clk_init_data {
|
||||
*/
|
||||
struct clk_hw {
|
||||
struct clk *clk;
|
||||
struct clk_init_data *init;
|
||||
const struct clk_init_data *init;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -171,6 +172,8 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
unsigned long fixed_rate);
|
||||
|
||||
void of_fixed_clk_setup(struct device_node *np);
|
||||
|
||||
/**
|
||||
* struct clk_gate - gating clock
|
||||
*
|
||||
@ -203,6 +206,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
|
||||
void __iomem *reg, u8 bit_idx,
|
||||
u8 clk_gate_flags, spinlock_t *lock);
|
||||
|
||||
struct clk_div_table {
|
||||
unsigned int val;
|
||||
unsigned int div;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_divider - adjustable divider clock
|
||||
*
|
||||
@ -210,6 +218,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
|
||||
* @reg: register containing the divider
|
||||
* @shift: shift to the divider bit field
|
||||
* @width: width of the divider bit field
|
||||
* @table: array of value/divider pairs, last entry should have div = 0
|
||||
* @lock: register lock
|
||||
*
|
||||
* Clock with an adjustable divider affecting its output frequency. Implements
|
||||
@ -229,6 +238,7 @@ struct clk_divider {
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 flags;
|
||||
const struct clk_div_table *table;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
@ -240,6 +250,11 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, spinlock_t *lock);
|
||||
struct clk *clk_register_divider_table(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
||||
spinlock_t *lock);
|
||||
|
||||
/**
|
||||
* struct clk_mux - multiplexer clock
|
||||
@ -334,5 +349,19 @@ void __clk_unprepare(struct clk *clk);
|
||||
void __clk_reparent(struct clk *clk, struct clk *new_parent);
|
||||
unsigned long __clk_round_rate(struct clk *clk, unsigned long rate);
|
||||
|
||||
struct of_device_id;
|
||||
|
||||
typedef void (*of_clk_init_cb_t)(struct device_node *);
|
||||
|
||||
int of_clk_add_provider(struct device_node *np,
|
||||
struct clk *(*clk_src_get)(struct of_phandle_args *args,
|
||||
void *data),
|
||||
void *data);
|
||||
void of_clk_del_provider(struct device_node *np);
|
||||
struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
|
||||
void *data);
|
||||
const char *of_clk_get_parent_name(struct device_node *np, int index);
|
||||
void of_clk_init(const struct of_device_id *matches);
|
||||
|
||||
#endif /* CONFIG_COMMON_CLK */
|
||||
#endif /* CLK_PROVIDER_H */
|
||||
|
@ -12,6 +12,7 @@
|
||||
#ifndef __LINUX_CLK_H
|
||||
#define __LINUX_CLK_H
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
@ -310,4 +311,23 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id);
|
||||
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
|
||||
struct device *dev);
|
||||
|
||||
struct device_node;
|
||||
struct of_phandle_args;
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
|
||||
struct clk *of_clk_get(struct device_node *np, int index);
|
||||
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
|
||||
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec);
|
||||
#else
|
||||
static inline struct clk *of_clk_get(struct device_node *np, int index)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
static inline struct clk *of_clk_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
1
include/linux/platform_data/clk-integrator.h
Normal file
1
include/linux/platform_data/clk-integrator.h
Normal file
@ -0,0 +1 @@
|
||||
void integrator_clk_init(bool is_cp);
|
1
include/linux/platform_data/clk-u300.h
Normal file
1
include/linux/platform_data/clk-u300.h
Normal file
@ -0,0 +1 @@
|
||||
void __init u300_clk_init(void __iomem *base);
|
Loading…
Reference in New Issue
Block a user