The common clock framework changes for 3.10 include many fixes for

existing platforms, as well as adoption of the framework by new
 platforms and devices.  Some long-needed fixes to the core framework are
 here as well as new features such as improved initialization of clocks
 from DT as well as framework reentrancy for nested clock operations.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJRfqtLAAoJEDqPOy9afJhJsxwP/RLvfeeMIU3804ahVNK2C59h
 ehJ06ZP+b0u0A7+YSC7CX1pHXIFW+UoZgYLJiLdV2kEdpOIKMELZyUcEVB97u1Of
 TVlsmHfTLv2zVAq/LYRVSKFYeMUd/6RRoq7Cm6hoj638IVeXG7C+8pei2aVZe++t
 1ENmb4UGFJ7NLfpE5zQ3fEuIfHfuWA8Od6SmPaV/YG5Io8HgkDGF3/tCJURJGII6
 xLN2Rh8qbFktJLVvKe6yLyvUEZiWh8A6HNPyNiFYYGX11wU76zK2wMN3BW6Nn/kW
 3PubzISoKRaoCZvuVK+CoLWnhFl2LteFVVmL1TBc/jxJe6q+rLX33sXl1q9K+SLt
 POnHf/7nDyO3zbZWgfRR1r3FdeZqdLYw8HVsLcOKFcv9n1UligzuUNml5PklKwNh
 BDMmSo5ytS1QPV1e9ZtVrk6IyvDyrenwfDW1Mw43ST6D23FVrivywB4X9ur6WljI
 d1/CBvQXQZ11Hd4OAvqRL8QYFJvc5WlERjSd1j6I6XS6xioKOTKMkUC/KpRcCid9
 avA6mJ5k/a1jTojvh2wl37paI//OzY0VDlxRSeMZIu9Dsn29DnPlE5CLg535Ovu+
 mn9OtLFEDNnlgWCMQYUehGd7ITgtwrB/fxxNeBbMYjDz4AIirR2BIvMR7I8CMTQz
 M0rHu8NpwKH6eqC6kAup
 =+LO3
 -----END PGP SIGNATURE-----

Merge tag 'clk-for-linus-3.10' of git://git.linaro.org/people/mturquette/linux

Pull clock framework update from Michael Turquette:
 "The common clock framework changes for 3.10 include many fixes for
  existing platforms, as well as adoption of the framework by new
  platforms and devices.

  Some long-needed fixes to the core framework are here as well as new
  features such as improved initialization of clocks from DT as well as
  framework reentrancy for nested clock operations."

* tag 'clk-for-linus-3.10' of git://git.linaro.org/people/mturquette/linux: (44 commits)
  clk: add clk_ignore_unused option to keep boot clocks on
  clk: ux500: fix mismatched types
  clk: vexpress: Add separate SP810 driver
  clk: si5351: make clk-si5351 depend on CONFIG_OF
  clk: export __clk_get_flags for modular clock providers
  clk: vt8500: Missing breaks in vtwm_pll_round_rate/_set_rate.
  clk: sunxi: Unify oscillator clock
  clk: composite: allow fixed rates & fixed dividers
  clk: composite: rename 'div' references to 'rate'
  clk: add si5351 i2c common clock driver
  clk: add device tree fixed-factor-clock binding support
  clk: Properly handle notifier return values
  clk: ux500: abx500: Define clock tree for ab850x
  clk: ux500: Add support for sysctrl clocks
  clk: mvebu: Fix valid value range checking for cpu_freq_select
  clk: Fixup locking issues for clk_set_parent
  clk: Fixup errorhandling for clk_set_parent
  clk: Restructure code for __clk_reparent
  clk: sunxi: drop an unnecesary kmalloc
  clk: sunxi: drop CLK_IGNORE_UNUSED
  ...
This commit is contained in:
Linus Torvalds 2013-04-29 16:43:54 -07:00
commit 362ed48dee
48 changed files with 4520 additions and 306 deletions

View File

@ -0,0 +1,56 @@
Frequently asked questions about the sunxi clock system
=======================================================
This document contains useful bits of information that people tend to ask
about the sunxi clock system, as well as accompanying ASCII art when adequate.
Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the
system?
A: The 24MHz oscillator allows gating to save power. Indeed, if gated
carelessly the system would stop functioning, but with the right
steps, one can gate it and keep the system running. Consider this
simplified suspend example:
While the system is operational, you would see something like
24MHz 32kHz
|
PLL1
\
\_ CPU Mux
|
[CPU]
When you are about to suspend, you switch the CPU Mux to the 32kHz
oscillator:
24Mhz 32kHz
| |
PLL1 |
/
CPU Mux _/
|
[CPU]
Finally you can gate the main oscillator
32kHz
|
|
/
CPU Mux _/
|
[CPU]
Q: Were can I learn more about the sunxi clocks?
A: The linux-sunxi wiki contains a page documenting the clock registers,
you can find it at
http://linux-sunxi.org/A10/CCM
The authoritative source for information at this time is the ccmu driver
released by Allwinner, you can find it at
https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu

View File

@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw)
}; };
Below is a matrix detailing which clk_ops are mandatory based upon the Below is a matrix detailing which clk_ops are mandatory based upon the
hardware capbilities of that clock. A cell marked as "y" means hardware capabilities of that clock. A cell marked as "y" means
mandatory, a cell marked as "n" implies that either including that mandatory, a cell marked as "n" implies that either including that
callback is invalid or otherwise uneccesary. Empty cells are either callback is invalid or otherwise unnecessary. Empty cells are either
optional or must be evaluated on a case-by-case basis. optional or must be evaluated on a case-by-case basis.
clock hardware characteristics clock hardware characteristics
@ -231,3 +231,14 @@ To better enforce this policy, always follow this simple rule: any
statically initialized clock data MUST be defined in a separate file statically initialized clock data MUST be defined in a separate file
from the logic that implements its ops. Basically separate the logic from the logic that implements its ops. Basically separate the logic
from the data and all is well. from the data and all is well.
Part 6 - Disabling clock gating of unused clocks
Sometimes during development it can be useful to be able to bypass the
default disabling of unused clocks. For example, if drivers aren't enabling
clocks properly but rely on them being on from the bootloader, bypassing
the disabling means that the driver will remain functional while the issues
are sorted out.
To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
kernel.

View File

@ -0,0 +1,22 @@
Binding for the axi-clkgen clock generator
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be "adi,axi-clkgen".
- #clock-cells : from common clock binding; Should always be set to 0.
- reg : Address and length of the axi-clkgen register set.
- clocks : Phandle and clock specifier for the parent clock.
Optional properties:
- clock-output-names : From common clock binding.
Example:
clock@0xff000000 {
compatible = "adi,axi-clkgen";
#clock-cells = <0>;
reg = <0xff000000 0x1000>;
clocks = <&osc 1>;
};

View File

@ -0,0 +1,24 @@
Binding for simple fixed factor 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-factor-clock".
- #clock-cells : from common clock binding; shall be set to 0.
- clock-div: fixed divider.
- clock-mult: fixed multiplier.
- clocks: parent clock.
Optional properties:
- clock-output-names : From common clock binding.
Example:
clock {
compatible = "fixed-factor-clock";
clocks = <&parentclk>;
#clock-cells = <0>;
div = <2>;
mult = <1>;
};

View File

@ -0,0 +1,114 @@
Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator.
Reference
[1] Si5351A/B/C Data Sheet
http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
The Si5351a/b/c are programmable i2c clock generators with upto 8 output
clocks. Si5351a also has a reduced pin-count package (MSOP10) where only
3 output clocks are accessible. The internal structure of the clock
generators can be found in [1].
==I2C device node==
Required properties:
- compatible: shall be one of "silabs,si5351{a,a-msop,b,c}".
- reg: i2c device address, shall be 0x60 or 0x61.
- #clock-cells: from common clock binding; shall be set to 1.
- clocks: from common clock binding; list of parent clock
handles, shall be xtal reference clock or xtal and clkin for
si5351c only.
- #address-cells: shall be set to 1.
- #size-cells: shall be set to 0.
Optional properties:
- silabs,pll-source: pair of (number, source) for each pll. Allows
to overwrite clock source of pll A (number=0) or B (number=1).
==Child nodes==
Each of the clock outputs can be overwritten individually by
using a child node to the I2C device node. If a child node for a clock
output is not set, the eeprom configuration is not overwritten.
Required child node properties:
- reg: number of clock output.
Optional child node properties:
- silabs,clock-source: source clock of the output divider stage N, shall be
0 = multisynth N
1 = multisynth 0 for output clocks 0-3, else multisynth4
2 = xtal
3 = clkin (si5351c only)
- silabs,drive-strength: output drive strength in mA, shall be one of {2,4,6,8}.
- silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth
divider.
- silabs,pll-master: boolean, multisynth can change pll frequency.
==Example==
/* 25MHz reference crystal */
ref25: ref25M {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <25000000>;
};
i2c-master-node {
/* Si5351a msop10 i2c clock generator */
si5351a: clock-generator@60 {
compatible = "silabs,si5351a-msop";
reg = <0x60>;
#address-cells = <1>;
#size-cells = <0>;
#clock-cells = <1>;
/* connect xtal input to 25MHz reference */
clocks = <&ref25>;
/* connect xtal input as source of pll0 and pll1 */
silabs,pll-source = <0 0>, <1 0>;
/*
* overwrite clkout0 configuration with:
* - 8mA output drive strength
* - pll0 as clock source of multisynth0
* - multisynth0 as clock source of output divider
* - multisynth0 can change pll0
* - set initial clock frequency of 74.25MHz
*/
clkout0 {
reg = <0>;
silabs,drive-strength = <8>;
silabs,multisynth-source = <0>;
silabs,clock-source = <0>;
silabs,pll-master;
clock-frequency = <74250000>;
};
/*
* overwrite clkout1 configuration with:
* - 4mA output drive strength
* - pll1 as clock source of multisynth1
* - multisynth1 as clock source of output divider
* - multisynth1 can change pll1
*/
clkout1 {
reg = <1>;
silabs,drive-strength = <4>;
silabs,multisynth-source = <1>;
silabs,clock-source = <0>;
pll-master;
};
/*
* overwrite clkout2 configuration with:
* - xtal as clock source of output divider
*/
clkout2 {
reg = <2>;
silabs,clock-source = <2>;
};
};
};

View File

@ -0,0 +1,151 @@
Device Tree Clock bindings for arch-sunxi
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be one of the following:
"allwinner,sun4i-osc-clk" - for a gatable oscillator
"allwinner,sun4i-pll1-clk" - for the main PLL clock
"allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
"allwinner,sun4i-axi-clk" - for the AXI clock
"allwinner,sun4i-axi-gates-clk" - for the AXI gates
"allwinner,sun4i-ahb-clk" - for the AHB clock
"allwinner,sun4i-ahb-gates-clk" - for the AHB gates
"allwinner,sun4i-apb0-clk" - for the APB0 clock
"allwinner,sun4i-apb0-gates-clk" - for the APB0 gates
"allwinner,sun4i-apb1-clk" - for the APB1 clock
"allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing
"allwinner,sun4i-apb1-gates-clk" - for the APB1 gates
Required properties for all clocks:
- reg : shall be the control register address for the clock.
- clocks : shall be the input parent clock(s) phandle for the clock
- #clock-cells : from common clock binding; shall be set to 0 except for
"allwinner,sun4i-*-gates-clk" where it shall be set to 1
Additionally, "allwinner,sun4i-*-gates-clk" clocks require:
- clock-output-names : the corresponding gate names that the clock controls
For example:
osc24M: osc24M@01c20050 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-osc-clk";
reg = <0x01c20050 0x4>;
clocks = <&osc24M_fixed>;
};
pll1: pll1@01c20000 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
};
cpu: cpu@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>;
};
Gate clock outputs
The "allwinner,sun4i-*-gates-clk" clocks provide several gatable outputs;
their corresponding offsets as present on sun4i are listed below. Note that
some of these gates are not present on sun5i.
* AXI gates ("allwinner,sun4i-axi-gates-clk")
DRAM 0
* AHB gates ("allwinner,sun4i-ahb-gates-clk")
USB0 0
EHCI0 1
OHCI0 2*
EHCI1 3
OHCI1 4*
SS 5
DMA 6
BIST 7
MMC0 8
MMC1 9
MMC2 10
MMC3 11
MS 12**
NAND 13
SDRAM 14
ACE 16
EMAC 17
TS 18
SPI0 20
SPI1 21
SPI2 22
SPI3 23
PATA 24
SATA 25**
GPS 26*
VE 32
TVD 33
TVE0 34
TVE1 35
LCD0 36
LCD1 37
CSI0 40
CSI1 41
HDMI 43
DE_BE0 44
DE_BE1 45
DE_FE0 46
DE_FE1 47
MP 50
MALI400 52
* APB0 gates ("allwinner,sun4i-apb0-gates-clk")
CODEC 0
SPDIF 1*
AC97 2
IIS 3
PIO 5
IR0 6
IR1 7
KEYPAD 10
* APB1 gates ("allwinner,sun4i-apb1-gates-clk")
I2C0 0
I2C1 1
I2C2 2
CAN 4
SCR 5
PS20 6
PS21 7
UART0 16
UART1 17
UART2 18
UART3 19
UART4 20
UART5 21
UART6 22
UART7 23
Notation:
[*]: The datasheet didn't mention these, but they are present on AW code
[**]: The datasheet had this marked as "NC" but they are used on AW code

View File

@ -49,6 +49,7 @@ samsung Samsung Semiconductor
sbs Smart Battery System sbs Smart Battery System
schindler Schindler schindler Schindler
sil Silicon Image sil Silicon Image
silabs Silicon Laboratories
simtek simtek
sirf SiRF Technology, Inc. sirf SiRF Technology, Inc.
snps Synopsys, Inc. snps Synopsys, Inc.

View File

@ -44,6 +44,7 @@ parameter is applicable:
AVR32 AVR32 architecture is enabled. AVR32 AVR32 architecture is enabled.
AX25 Appropriate AX.25 support is enabled. AX25 Appropriate AX.25 support is enabled.
BLACKFIN Blackfin architecture is enabled. BLACKFIN Blackfin architecture is enabled.
CLK Common clock infrastructure is enabled.
DRM Direct Rendering Management support is enabled. DRM Direct Rendering Management support is enabled.
DYNAMIC_DEBUG Build in debug messages and enable them at runtime DYNAMIC_DEBUG Build in debug messages and enable them at runtime
EDD BIOS Enhanced Disk Drive Services (EDD) is enabled EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
@ -472,6 +473,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
cio_ignore= [S390] cio_ignore= [S390]
See Documentation/s390/CommonIO for details. See Documentation/s390/CommonIO for details.
clk_ignore_unused
[CLK]
Keep all clocks already enabled by bootloader on,
even if no driver has claimed them. This is useful
for debug and development, but should not be
needed on a platform with proper driver support.
For more information, see Documentation/clk.txt.
clock= [BUGS=X86-32, HW] gettimeofday clocksource override. clock= [BUGS=X86-32, HW] gettimeofday clocksource override.
[Deprecated] [Deprecated]

View File

@ -169,7 +169,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
busy->mux.reg = reg; busy->mux.reg = reg;
busy->mux.shift = shift; busy->mux.shift = shift;
busy->mux.width = width; busy->mux.mask = BIT(width) - 1;
busy->mux.lock = &imx_ccm_lock; busy->mux.lock = &imx_ccm_lock;
busy->mux_ops = &clk_mux_ops; busy->mux_ops = &clk_mux_ops;

View File

@ -21,6 +21,8 @@
#include <linux/regulator/fixed.h> #include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/vexpress.h> #include <linux/vexpress.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <asm/arch_timer.h> #include <asm/arch_timer.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
@ -433,7 +435,7 @@ static void __init v2m_dt_timer_init(void)
{ {
struct device_node *node = NULL; struct device_node *node = NULL;
vexpress_clk_of_init(); of_clk_init(NULL);
do { do {
node = of_find_compatible_node(node, NULL, "arm,sp804"); node = of_find_compatible_node(node, NULL, "arm,sp804");
@ -441,6 +443,10 @@ static void __init v2m_dt_timer_init(void)
if (node) { if (node) {
pr_info("Using SP804 '%s' as a clock & events source\n", pr_info("Using SP804 '%s' as a clock & events source\n",
node->full_name); node->full_name);
WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
"timclken1"), "v2m-timer0", "sp804"));
WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
"timclken2"), "v2m-timer1", "sp804"));
v2m_sp804_init(of_iomap(node, 0), v2m_sp804_init(of_iomap(node, 0),
irq_of_parse_and_map(node, 0)); irq_of_parse_and_map(node, 0));
} }

View File

@ -55,6 +55,16 @@ config COMMON_CLK_MAX77686
---help--- ---help---
This driver supports Maxim 77686 crystal oscillator clock. This driver supports Maxim 77686 crystal oscillator clock.
config COMMON_CLK_SI5351
tristate "Clock driver for SiLabs 5351A/B/C"
depends on I2C
depends on OF
select REGMAP_I2C
select RATIONAL
---help---
This driver supports Silicon Labs 5351A/B/C programmable clock
generators.
config CLK_TWL6040 config CLK_TWL6040
tristate "External McPDM functional clock from twl6040" tristate "External McPDM functional clock from twl6040"
depends on TWL6040_CORE depends on TWL6040_CORE
@ -63,6 +73,14 @@ config CLK_TWL6040
McPDM. McPDM module is using the external bit clock on the McPDM bus McPDM. McPDM module is using the external bit clock on the McPDM bus
as functional clock. as functional clock.
config COMMON_CLK_AXI_CLKGEN
tristate "AXI clkgen driver"
depends on ARCH_ZYNQ || MICROBLAZE
help
---help---
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
FPGAs. It is commonly used in Analog Devices' reference designs.
endmenu endmenu
source "drivers/clk/mvebu/Kconfig" source "drivers/clk/mvebu/Kconfig"

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
# SoCs specific # SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/ obj-$(CONFIG_ARCH_MMP) += mmp/
endif endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
@ -31,6 +33,8 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_X86) += x86/
# Chip specific # Chip specific
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o

View File

@ -0,0 +1,331 @@
/*
* AXI clkgen driver
*
* Copyright 2012-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*
*/
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/err.h>
#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
#define AXI_CLKGEN_REG_CLK_OUT1 0x08
#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
#define AXI_CLKGEN_REG_CLK_DIV 0x10
#define AXI_CLKGEN_REG_CLK_FB1 0x14
#define AXI_CLKGEN_REG_CLK_FB2 0x18
#define AXI_CLKGEN_REG_LOCK1 0x1c
#define AXI_CLKGEN_REG_LOCK2 0x20
#define AXI_CLKGEN_REG_LOCK3 0x24
#define AXI_CLKGEN_REG_FILTER1 0x28
#define AXI_CLKGEN_REG_FILTER2 0x2c
struct axi_clkgen {
void __iomem *base;
struct clk_hw clk_hw;
};
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
{
switch (m) {
case 0:
return 0x01001990;
case 1:
return 0x01001190;
case 2:
return 0x01009890;
case 3:
return 0x01001890;
case 4:
return 0x01008890;
case 5 ... 8:
return 0x01009090;
case 9 ... 11:
return 0x01000890;
case 12:
return 0x08009090;
case 13 ... 22:
return 0x01001090;
case 23 ... 36:
return 0x01008090;
case 37 ... 46:
return 0x08001090;
default:
return 0x08008090;
}
}
static const uint32_t axi_clkgen_lock_table[] = {
0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
};
static uint32_t axi_clkgen_lookup_lock(unsigned int m)
{
if (m < ARRAY_SIZE(axi_clkgen_lock_table))
return axi_clkgen_lock_table[m];
return 0x1f1f00fa;
}
static const unsigned int fpfd_min = 10000;
static const unsigned int fpfd_max = 300000;
static const unsigned int fvco_min = 600000;
static const unsigned int fvco_max = 1200000;
static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
{
unsigned long d, d_min, d_max, _d_min, _d_max;
unsigned long m, m_min, m_max;
unsigned long f, dout, best_f, fvco;
fin /= 1000;
fout /= 1000;
best_f = ULONG_MAX;
*best_d = 0;
*best_m = 0;
*best_dout = 0;
d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
d_max = min_t(unsigned long, fin / fpfd_min, 80);
m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
for (m = m_min; m <= m_max; m++) {
_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
_d_max = min(d_max, fin * m / fvco_min);
for (d = _d_min; d <= _d_max; d++) {
fvco = fin * m / d;
dout = DIV_ROUND_CLOSEST(fvco, fout);
dout = clamp_t(unsigned long, dout, 1, 128);
f = fvco / dout;
if (abs(f - fout) < abs(best_f - fout)) {
best_f = f;
*best_d = d;
*best_m = m;
*best_dout = dout;
if (best_f == fout)
return;
}
}
}
}
static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
unsigned int *high, unsigned int *edge, unsigned int *nocount)
{
if (divider == 1)
*nocount = 1;
else
*nocount = 0;
*high = divider / 2;
*edge = divider % 2;
*low = divider - *high;
}
static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int val)
{
writel(val, axi_clkgen->base + reg);
}
static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int *val)
{
*val = readl(axi_clkgen->base + reg);
}
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct axi_clkgen, clk_hw);
}
static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
unsigned long rate, unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
unsigned int nocount;
unsigned int high;
unsigned int edge;
unsigned int low;
uint32_t filter;
uint32_t lock;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
filter = axi_clkgen_lookup_filter(m - 1);
lock = axi_clkgen_lookup_lock(m - 1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
(edge << 7) | (nocount << 6));
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
(edge << 13) | (nocount << 12) | (high << 6) | low);
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
(edge << 7) | (nocount << 6));
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
(((lock >> 16) & 0x1f) << 10) | 0x1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
(((lock >> 24) & 0x1f) << 10) | 0x3e9);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
return 0;
}
static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned int d, m, dout;
axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
return *parent_rate / d * m / dout;
}
static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
unsigned int reg;
unsigned long long tmp;
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
if (d == 0 || dout == 0)
return 0;
tmp = (unsigned long long)(parent_rate / d) * m;
do_div(tmp, dout);
if (tmp > ULONG_MAX)
return ULONG_MAX;
return tmp;
}
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate,
};
static int axi_clkgen_probe(struct platform_device *pdev)
{
struct axi_clkgen *axi_clkgen;
struct clk_init_data init;
const char *parent_name;
const char *clk_name;
struct resource *mem;
struct clk *clk;
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
if (!axi_clkgen)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(axi_clkgen->base))
return PTR_ERR(axi_clkgen->base);
parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
if (!parent_name)
return -EINVAL;
clk_name = pdev->dev.of_node->name;
of_property_read_string(pdev->dev.of_node, "clock-output-names",
&clk_name);
init.name = clk_name;
init.ops = &axi_clkgen_ops;
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
axi_clkgen->clk_hw.init = &init;
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
clk);
}
static int axi_clkgen_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
return 0;
}
static const struct of_device_id axi_clkgen_ids[] = {
{ .compatible = "adi,axi-clkgen-1.00.a" },
{ },
};
MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
.owner = THIS_MODULE,
.of_match_table = axi_clkgen_ids,
},
.probe = axi_clkgen_probe,
.remove = axi_clkgen_remove,
};
module_platform_driver(axi_clkgen_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");

210
drivers/clk/clk-composite.c Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* 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/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/slab.h>
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
static u8 clk_composite_get_parent(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->get_parent(mux_hw);
}
static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->set_parent(mux_hw, index);
}
static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *rate_ops = composite->rate_ops;
struct clk_hw *rate_hw = composite->rate_hw;
rate_hw->clk = hw->clk;
return rate_ops->recalc_rate(rate_hw, parent_rate);
}
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *rate_ops = composite->rate_ops;
struct clk_hw *rate_hw = composite->rate_hw;
rate_hw->clk = hw->clk;
return rate_ops->round_rate(rate_hw, rate, prate);
}
static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *rate_ops = composite->rate_ops;
struct clk_hw *rate_hw = composite->rate_hw;
rate_hw->clk = hw->clk;
return rate_ops->set_rate(rate_hw, rate, parent_rate);
}
static int clk_composite_is_enabled(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->is_enabled(gate_hw);
}
static int clk_composite_enable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->enable(gate_hw);
}
static void clk_composite_disable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
gate_ops->disable(gate_hw);
}
struct clk *clk_register_composite(struct device *dev, const char *name,
const char **parent_names, int num_parents,
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
struct clk_composite *composite;
struct clk_ops *clk_composite_ops;
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
if (!composite) {
pr_err("%s: could not allocate composite clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = parent_names;
init.num_parents = num_parents;
clk_composite_ops = &composite->ops;
if (mux_hw && mux_ops) {
if (!mux_ops->get_parent || !mux_ops->set_parent) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->mux_hw = mux_hw;
composite->mux_ops = mux_ops;
clk_composite_ops->get_parent = clk_composite_get_parent;
clk_composite_ops->set_parent = clk_composite_set_parent;
}
if (rate_hw && rate_ops) {
if (!rate_ops->recalc_rate) {
clk = ERR_PTR(-EINVAL);
goto err;
}
/* .round_rate is a prerequisite for .set_rate */
if (rate_ops->round_rate) {
clk_composite_ops->round_rate = clk_composite_round_rate;
if (rate_ops->set_rate) {
clk_composite_ops->set_rate = clk_composite_set_rate;
}
} else {
WARN(rate_ops->set_rate,
"%s: missing round_rate op is required\n",
__func__);
}
composite->rate_hw = rate_hw;
composite->rate_ops = rate_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
}
if (gate_hw && gate_ops) {
if (!gate_ops->is_enabled || !gate_ops->enable ||
!gate_ops->disable) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->gate_hw = gate_hw;
composite->gate_ops = gate_ops;
clk_composite_ops->is_enabled = clk_composite_is_enabled;
clk_composite_ops->enable = clk_composite_enable;
clk_composite_ops->disable = clk_composite_disable;
}
init.ops = clk_composite_ops;
composite->hw.init = &init;
clk = clk_register(dev, &composite->hw);
if (IS_ERR(clk))
goto err;
if (composite->mux_hw)
composite->mux_hw->clk = clk;
if (composite->rate_hw)
composite->rate_hw->clk = clk;
if (composite->gate_hw)
composite->gate_hw->clk = clk;
return clk;
err:
kfree(composite);
return clk;
}

View File

@ -109,7 +109,8 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
div = _get_div(divider, val); div = _get_div(divider, val);
if (!div) { if (!div) {
WARN(1, "%s: Invalid divisor for clock %s\n", __func__, WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
__clk_get_name(hw->clk)); __clk_get_name(hw->clk));
return parent_rate; return parent_rate;
} }

View File

@ -11,6 +11,7 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h>
/* /*
* DOC: basic fixed multiplier and divider clock that cannot gate * DOC: basic fixed multiplier and divider clock that cannot gate
@ -96,3 +97,38 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
return clk; return clk;
} }
#ifdef CONFIG_OF
/**
* of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
*/
void __init of_fixed_factor_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent_name;
u32 div, mult;
if (of_property_read_u32(node, "clock-div", &div)) {
pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
__func__, node->name);
return;
}
if (of_property_read_u32(node, "clock-mult", &mult)) {
pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n",
__func__, node->name);
return;
}
of_property_read_string(node, "clock-output-names", &clk_name);
parent_name = of_clk_get_parent_name(node, 0);
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
mult, div);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
of_fixed_factor_clk_setup);
#endif

View File

@ -32,6 +32,7 @@
static u8 clk_mux_get_parent(struct clk_hw *hw) static u8 clk_mux_get_parent(struct clk_hw *hw)
{ {
struct clk_mux *mux = to_clk_mux(hw); struct clk_mux *mux = to_clk_mux(hw);
int num_parents = __clk_get_num_parents(hw->clk);
u32 val; u32 val;
/* /*
@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
* val = 0x4 really means "bit 2, index starts at bit 0" * val = 0x4 really means "bit 2, index starts at bit 0"
*/ */
val = readl(mux->reg) >> mux->shift; val = readl(mux->reg) >> mux->shift;
val &= (1 << mux->width) - 1; val &= mux->mask;
if (mux->table) {
int i;
for (i = 0; i < num_parents; i++)
if (mux->table[i] == val)
return i;
return -EINVAL;
}
if (val && (mux->flags & CLK_MUX_INDEX_BIT)) if (val && (mux->flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1; val = ffs(val) - 1;
@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
if (val && (mux->flags & CLK_MUX_INDEX_ONE)) if (val && (mux->flags & CLK_MUX_INDEX_ONE))
val--; val--;
if (val >= __clk_get_num_parents(hw->clk)) if (val >= num_parents)
return -EINVAL; return -EINVAL;
return val; return val;
@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
u32 val; u32 val;
unsigned long flags = 0; unsigned long flags = 0;
if (mux->table)
index = mux->table[index];
else {
if (mux->flags & CLK_MUX_INDEX_BIT) if (mux->flags & CLK_MUX_INDEX_BIT)
index = (1 << ffs(index)); index = (1 << ffs(index));
if (mux->flags & CLK_MUX_INDEX_ONE) if (mux->flags & CLK_MUX_INDEX_ONE)
index++; index++;
}
if (mux->lock) if (mux->lock)
spin_lock_irqsave(mux->lock, flags); spin_lock_irqsave(mux->lock, flags);
val = readl(mux->reg); val = readl(mux->reg);
val &= ~(((1 << mux->width) - 1) << mux->shift); val &= ~(mux->mask << mux->shift);
val |= index << mux->shift; val |= index << mux->shift;
writel(val, mux->reg); writel(val, mux->reg);
@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = {
}; };
EXPORT_SYMBOL_GPL(clk_mux_ops); EXPORT_SYMBOL_GPL(clk_mux_ops);
struct clk *clk_register_mux(struct device *dev, const char *name, struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags, const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width, void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, spinlock_t *lock) u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{ {
struct clk_mux *mux; struct clk_mux *mux;
struct clk *clk; struct clk *clk;
@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
/* struct clk_mux assignments */ /* struct clk_mux assignments */
mux->reg = reg; mux->reg = reg;
mux->shift = shift; mux->shift = shift;
mux->width = width; mux->mask = mask;
mux->flags = clk_mux_flags; mux->flags = clk_mux_flags;
mux->lock = lock; mux->lock = lock;
mux->table = table;
mux->hw.init = &init; mux->hw.init = &init;
clk = clk_register(dev, &mux->hw); clk = clk_register(dev, &mux->hw);
@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
return clk; return clk;
} }
struct clk *clk_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
{
u32 mask = BIT(width) - 1;
return clk_register_mux_table(dev, name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags,
NULL, lock);
}

View File

@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void)
for (i = pll1; i < maxclk; i++) { for (i = pll1; i < maxclk; i++) {
prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
BUG_ON(!prima2_clks[i]); BUG_ON(IS_ERR(prima2_clks[i]));
} }
clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
clk_register_clkdev(prima2_clks[io], NULL, "io"); clk_register_clkdev(prima2_clks[io], NULL, "io");

1510
drivers/clk/clk-si5351.c Normal file

File diff suppressed because it is too large Load Diff

155
drivers/clk/clk-si5351.h Normal file
View File

@ -0,0 +1,155 @@
/*
* clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Rabeeh Khoury <rabeeh@solid-run.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.
*/
#ifndef _CLK_SI5351_H_
#define _CLK_SI5351_H_
#define SI5351_BUS_BASE_ADDR 0x60
#define SI5351_PLL_VCO_MIN 600000000
#define SI5351_PLL_VCO_MAX 900000000
#define SI5351_MULTISYNTH_MIN_FREQ 1000000
#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000
#define SI5351_MULTISYNTH_MAX_FREQ 160000000
#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ
#define SI5351_CLKOUT_MIN_FREQ 8000
#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ
#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ
#define SI5351_PLL_A_MIN 15
#define SI5351_PLL_A_MAX 90
#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1)
#define SI5351_PLL_C_MAX 1048575
#define SI5351_MULTISYNTH_A_MIN 6
#define SI5351_MULTISYNTH_A_MAX 1800
#define SI5351_MULTISYNTH67_A_MAX 254
#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1)
#define SI5351_MULTISYNTH_C_MAX 1048575
#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1)
#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1)
#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1)
#define SI5351_DEVICE_STATUS 0
#define SI5351_INTERRUPT_STATUS 1
#define SI5351_INTERRUPT_MASK 2
#define SI5351_STATUS_SYS_INIT (1<<7)
#define SI5351_STATUS_LOL_B (1<<6)
#define SI5351_STATUS_LOL_A (1<<5)
#define SI5351_STATUS_LOS (1<<4)
#define SI5351_OUTPUT_ENABLE_CTRL 3
#define SI5351_OEB_PIN_ENABLE_CTRL 9
#define SI5351_PLL_INPUT_SOURCE 15
#define SI5351_CLKIN_DIV_MASK (3<<6)
#define SI5351_CLKIN_DIV_1 (0<<6)
#define SI5351_CLKIN_DIV_2 (1<<6)
#define SI5351_CLKIN_DIV_4 (2<<6)
#define SI5351_CLKIN_DIV_8 (3<<6)
#define SI5351_PLLB_SOURCE (1<<3)
#define SI5351_PLLA_SOURCE (1<<2)
#define SI5351_CLK0_CTRL 16
#define SI5351_CLK1_CTRL 17
#define SI5351_CLK2_CTRL 18
#define SI5351_CLK3_CTRL 19
#define SI5351_CLK4_CTRL 20
#define SI5351_CLK5_CTRL 21
#define SI5351_CLK6_CTRL 22
#define SI5351_CLK7_CTRL 23
#define SI5351_CLK_POWERDOWN (1<<7)
#define SI5351_CLK_INTEGER_MODE (1<<6)
#define SI5351_CLK_PLL_SELECT (1<<5)
#define SI5351_CLK_INVERT (1<<4)
#define SI5351_CLK_INPUT_MASK (3<<2)
#define SI5351_CLK_INPUT_XTAL (0<<2)
#define SI5351_CLK_INPUT_CLKIN (1<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
#define SI5351_CLK3_0_DISABLE_STATE 24
#define SI5351_CLK7_4_DISABLE_STATE 25
#define SI5351_CLK_DISABLE_STATE_LOW 0
#define SI5351_CLK_DISABLE_STATE_HIGH 1
#define SI5351_CLK_DISABLE_STATE_FLOAT 2
#define SI5351_CLK_DISABLE_STATE_NEVER 3
#define SI5351_PARAMETERS_LENGTH 8
#define SI5351_PLLA_PARAMETERS 26
#define SI5351_PLLB_PARAMETERS 34
#define SI5351_CLK0_PARAMETERS 42
#define SI5351_CLK1_PARAMETERS 50
#define SI5351_CLK2_PARAMETERS 58
#define SI5351_CLK3_PARAMETERS 66
#define SI5351_CLK4_PARAMETERS 74
#define SI5351_CLK5_PARAMETERS 82
#define SI5351_CLK6_PARAMETERS 90
#define SI5351_CLK7_PARAMETERS 91
#define SI5351_CLK6_7_OUTPUT_DIVIDER 92
#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4)
#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0)
#define SI5351_OUTPUT_CLK_DIV_SHIFT 4
#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0
#define SI5351_OUTPUT_CLK_DIV_1 0
#define SI5351_OUTPUT_CLK_DIV_2 1
#define SI5351_OUTPUT_CLK_DIV_4 2
#define SI5351_OUTPUT_CLK_DIV_8 3
#define SI5351_OUTPUT_CLK_DIV_16 4
#define SI5351_OUTPUT_CLK_DIV_32 5
#define SI5351_OUTPUT_CLK_DIV_64 6
#define SI5351_OUTPUT_CLK_DIV_128 7
#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2)
#define SI5351_SSC_PARAM0 149
#define SI5351_SSC_PARAM1 150
#define SI5351_SSC_PARAM2 151
#define SI5351_SSC_PARAM3 152
#define SI5351_SSC_PARAM4 153
#define SI5351_SSC_PARAM5 154
#define SI5351_SSC_PARAM6 155
#define SI5351_SSC_PARAM7 156
#define SI5351_SSC_PARAM8 157
#define SI5351_SSC_PARAM9 158
#define SI5351_SSC_PARAM10 159
#define SI5351_SSC_PARAM11 160
#define SI5351_SSC_PARAM12 161
#define SI5351_VXCO_PARAMETERS_LOW 162
#define SI5351_VXCO_PARAMETERS_MID 163
#define SI5351_VXCO_PARAMETERS_HIGH 164
#define SI5351_CLK0_PHASE_OFFSET 165
#define SI5351_CLK1_PHASE_OFFSET 166
#define SI5351_CLK2_PHASE_OFFSET 167
#define SI5351_CLK3_PHASE_OFFSET 168
#define SI5351_CLK4_PHASE_OFFSET 169
#define SI5351_CLK5_PHASE_OFFSET 170
#define SI5351_PLL_RESET 177
#define SI5351_PLL_RESET_B (1<<7)
#define SI5351_PLL_RESET_A (1<<5)
#define SI5351_CRYSTAL_LOAD 183
#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
#define SI5351_FANOUT_ENABLE 187
#define SI5351_CLKIN_ENABLE (1<<7)
#define SI5351_XTAL_ENABLE (1<<6)
#define SI5351_MULTISYNTH_ENABLE (1<<4)
#endif

View File

@ -488,6 +488,7 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
case PLL_TYPE_WM8750: case PLL_TYPE_WM8750:
wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
break;
default: default:
pr_err("%s: invalid pll type\n", __func__); pr_err("%s: invalid pll type\n", __func__);
return 0; return 0;
@ -523,6 +524,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
case PLL_TYPE_WM8750: case PLL_TYPE_WM8750:
wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
break;
default: default:
round_rate = 0; round_rate = 0;
} }

View File

@ -20,6 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/clk/zynq.h>
static void __iomem *slcr_base; static void __iomem *slcr_base;

View File

@ -19,14 +19,77 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h>
static DEFINE_SPINLOCK(enable_lock); static DEFINE_SPINLOCK(enable_lock);
static DEFINE_MUTEX(prepare_lock); static DEFINE_MUTEX(prepare_lock);
static struct task_struct *prepare_owner;
static struct task_struct *enable_owner;
static int prepare_refcnt;
static int enable_refcnt;
static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list); static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list); static LIST_HEAD(clk_notifier_list);
/*** locking ***/
static void clk_prepare_lock(void)
{
if (!mutex_trylock(&prepare_lock)) {
if (prepare_owner == current) {
prepare_refcnt++;
return;
}
mutex_lock(&prepare_lock);
}
WARN_ON_ONCE(prepare_owner != NULL);
WARN_ON_ONCE(prepare_refcnt != 0);
prepare_owner = current;
prepare_refcnt = 1;
}
static void clk_prepare_unlock(void)
{
WARN_ON_ONCE(prepare_owner != current);
WARN_ON_ONCE(prepare_refcnt == 0);
if (--prepare_refcnt)
return;
prepare_owner = NULL;
mutex_unlock(&prepare_lock);
}
static unsigned long clk_enable_lock(void)
{
unsigned long flags;
if (!spin_trylock_irqsave(&enable_lock, flags)) {
if (enable_owner == current) {
enable_refcnt++;
return flags;
}
spin_lock_irqsave(&enable_lock, flags);
}
WARN_ON_ONCE(enable_owner != NULL);
WARN_ON_ONCE(enable_refcnt != 0);
enable_owner = current;
enable_refcnt = 1;
return flags;
}
static void clk_enable_unlock(unsigned long flags)
{
WARN_ON_ONCE(enable_owner != current);
WARN_ON_ONCE(enable_refcnt == 0);
if (--enable_refcnt)
return;
enable_owner = NULL;
spin_unlock_irqrestore(&enable_lock, flags);
}
/*** debugfs support ***/ /*** debugfs support ***/
#ifdef CONFIG_COMMON_CLK_DEBUG #ifdef CONFIG_COMMON_CLK_DEBUG
@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, " clock enable_cnt prepare_cnt rate\n");
seq_printf(s, "---------------------------------------------------------------------\n"); seq_printf(s, "---------------------------------------------------------------------\n");
mutex_lock(&prepare_lock); clk_prepare_lock();
hlist_for_each_entry(c, &clk_root_list, child_node) hlist_for_each_entry(c, &clk_root_list, child_node)
clk_summary_show_subtree(s, c, 0); clk_summary_show_subtree(s, c, 0);
@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
hlist_for_each_entry(c, &clk_orphan_list, child_node) hlist_for_each_entry(c, &clk_orphan_list, child_node)
clk_summary_show_subtree(s, c, 0); clk_summary_show_subtree(s, c, 0);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return 0; return 0;
} }
@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data)
seq_printf(s, "{"); seq_printf(s, "{");
mutex_lock(&prepare_lock); clk_prepare_lock();
hlist_for_each_entry(c, &clk_root_list, child_node) { hlist_for_each_entry(c, &clk_root_list, child_node) {
if (!first_node) if (!first_node)
@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data)
clk_dump_subtree(s, c, 0); clk_dump_subtree(s, c, 0);
} }
mutex_unlock(&prepare_lock); clk_prepare_unlock();
seq_printf(s, "}"); seq_printf(s, "}");
return 0; return 0;
@ -279,6 +342,39 @@ out:
return ret; return ret;
} }
/**
* clk_debug_reparent - reparent clk node in the debugfs clk tree
* @clk: the clk being reparented
* @new_parent: the new clk parent, may be NULL
*
* Rename clk entry in the debugfs clk tree if debugfs has been
* initialized. Otherwise it bails out early since the debugfs clk tree
* will be created lazily by clk_debug_init as part of a late_initcall.
*
* Caller must hold prepare_lock.
*/
static void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
{
struct dentry *d;
struct dentry *new_parent_d;
if (!inited)
return;
if (new_parent)
new_parent_d = new_parent->dentry;
else
new_parent_d = orphandir;
d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
new_parent_d, clk->name);
if (d)
clk->dentry = d;
else
pr_debug("%s: failed to rename debugfs entry for %s\n",
__func__, clk->name);
}
/** /**
* clk_debug_init - lazily create the debugfs clk tree visualization * clk_debug_init - lazily create the debugfs clk tree visualization
* *
@ -316,7 +412,7 @@ static int __init clk_debug_init(void)
if (!orphandir) if (!orphandir)
return -ENOMEM; return -ENOMEM;
mutex_lock(&prepare_lock); clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node) hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_debug_create_subtree(clk, rootdir); clk_debug_create_subtree(clk, rootdir);
@ -326,15 +422,44 @@ static int __init clk_debug_init(void)
inited = 1; inited = 1;
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return 0; return 0;
} }
late_initcall(clk_debug_init); late_initcall(clk_debug_init);
#else #else
static inline int clk_debug_register(struct clk *clk) { return 0; } static inline int clk_debug_register(struct clk *clk) { return 0; }
static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
{
}
#endif #endif
/* caller must hold prepare_lock */
static void clk_unprepare_unused_subtree(struct clk *clk)
{
struct clk *child;
if (!clk)
return;
hlist_for_each_entry(child, &clk->children, child_node)
clk_unprepare_unused_subtree(child);
if (clk->prepare_count)
return;
if (clk->flags & CLK_IGNORE_UNUSED)
return;
if (__clk_is_prepared(clk)) {
if (clk->ops->unprepare_unused)
clk->ops->unprepare_unused(clk->hw);
else if (clk->ops->unprepare)
clk->ops->unprepare(clk->hw);
}
}
EXPORT_SYMBOL_GPL(__clk_get_flags);
/* caller must hold prepare_lock */ /* caller must hold prepare_lock */
static void clk_disable_unused_subtree(struct clk *clk) static void clk_disable_unused_subtree(struct clk *clk)
{ {
@ -347,7 +472,7 @@ static void clk_disable_unused_subtree(struct clk *clk)
hlist_for_each_entry(child, &clk->children, child_node) hlist_for_each_entry(child, &clk->children, child_node)
clk_disable_unused_subtree(child); clk_disable_unused_subtree(child);
spin_lock_irqsave(&enable_lock, flags); flags = clk_enable_lock();
if (clk->enable_count) if (clk->enable_count)
goto unlock_out; goto unlock_out;
@ -368,17 +493,30 @@ static void clk_disable_unused_subtree(struct clk *clk)
} }
unlock_out: unlock_out:
spin_unlock_irqrestore(&enable_lock, flags); clk_enable_unlock(flags);
out: out:
return; return;
} }
static bool clk_ignore_unused;
static int __init clk_ignore_unused_setup(char *__unused)
{
clk_ignore_unused = true;
return 1;
}
__setup("clk_ignore_unused", clk_ignore_unused_setup);
static int clk_disable_unused(void) static int clk_disable_unused(void)
{ {
struct clk *clk; struct clk *clk;
mutex_lock(&prepare_lock); if (clk_ignore_unused) {
pr_warn("clk: Not disabling unused clocks\n");
return 0;
}
clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node) hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_disable_unused_subtree(clk); clk_disable_unused_subtree(clk);
@ -386,7 +524,13 @@ static int clk_disable_unused(void)
hlist_for_each_entry(clk, &clk_orphan_list, child_node) hlist_for_each_entry(clk, &clk_orphan_list, child_node)
clk_disable_unused_subtree(clk); clk_disable_unused_subtree(clk);
mutex_unlock(&prepare_lock); hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_unprepare_unused_subtree(clk);
hlist_for_each_entry(clk, &clk_orphan_list, child_node)
clk_unprepare_unused_subtree(clk);
clk_prepare_unlock();
return 0; return 0;
} }
@ -451,6 +595,27 @@ unsigned long __clk_get_flags(struct clk *clk)
return !clk ? 0 : clk->flags; return !clk ? 0 : clk->flags;
} }
bool __clk_is_prepared(struct clk *clk)
{
int ret;
if (!clk)
return false;
/*
* .is_prepared is optional for clocks that can prepare
* fall back to software usage counter if it is missing
*/
if (!clk->ops->is_prepared) {
ret = clk->prepare_count ? 1 : 0;
goto out;
}
ret = clk->ops->is_prepared(clk->hw);
out:
return !!ret;
}
bool __clk_is_enabled(struct clk *clk) bool __clk_is_enabled(struct clk *clk)
{ {
int ret; int ret;
@ -548,9 +713,9 @@ void __clk_unprepare(struct clk *clk)
*/ */
void clk_unprepare(struct clk *clk) void clk_unprepare(struct clk *clk)
{ {
mutex_lock(&prepare_lock); clk_prepare_lock();
__clk_unprepare(clk); __clk_unprepare(clk);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
} }
EXPORT_SYMBOL_GPL(clk_unprepare); EXPORT_SYMBOL_GPL(clk_unprepare);
@ -596,9 +761,9 @@ int clk_prepare(struct clk *clk)
{ {
int ret; int ret;
mutex_lock(&prepare_lock); clk_prepare_lock();
ret = __clk_prepare(clk); ret = __clk_prepare(clk);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -640,9 +805,9 @@ void clk_disable(struct clk *clk)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&enable_lock, flags); flags = clk_enable_lock();
__clk_disable(clk); __clk_disable(clk);
spin_unlock_irqrestore(&enable_lock, flags); clk_enable_unlock(flags);
} }
EXPORT_SYMBOL_GPL(clk_disable); EXPORT_SYMBOL_GPL(clk_disable);
@ -693,9 +858,9 @@ int clk_enable(struct clk *clk)
unsigned long flags; unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&enable_lock, flags); flags = clk_enable_lock();
ret = __clk_enable(clk); ret = __clk_enable(clk);
spin_unlock_irqrestore(&enable_lock, flags); clk_enable_unlock(flags);
return ret; return ret;
} }
@ -740,9 +905,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
{ {
unsigned long ret; unsigned long ret;
mutex_lock(&prepare_lock); clk_prepare_lock();
ret = __clk_round_rate(clk, rate); ret = __clk_round_rate(clk, rate);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -837,13 +1002,13 @@ unsigned long clk_get_rate(struct clk *clk)
{ {
unsigned long rate; unsigned long rate;
mutex_lock(&prepare_lock); clk_prepare_lock();
if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
__clk_recalc_rates(clk, 0); __clk_recalc_rates(clk, 0);
rate = __clk_get_rate(clk); rate = __clk_get_rate(clk);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return rate; return rate;
} }
@ -876,16 +1041,16 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
else else
new_rate = parent_rate; new_rate = parent_rate;
/* abort the rate change if a driver returns NOTIFY_BAD */ /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
if (clk->notifier_count) if (clk->notifier_count)
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
if (ret == NOTIFY_BAD) if (ret & NOTIFY_STOP_MASK)
goto out; goto out;
hlist_for_each_entry(child, &clk->children, child_node) { hlist_for_each_entry(child, &clk->children, child_node) {
ret = __clk_speculate_rates(child, new_rate); ret = __clk_speculate_rates(child, new_rate);
if (ret == NOTIFY_BAD) if (ret & NOTIFY_STOP_MASK)
break; break;
} }
@ -974,11 +1139,11 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
int ret = NOTIFY_DONE; int ret = NOTIFY_DONE;
if (clk->rate == clk->new_rate) if (clk->rate == clk->new_rate)
return 0; return NULL;
if (clk->notifier_count) { if (clk->notifier_count) {
ret = __clk_notify(clk, event, clk->rate, clk->new_rate); ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
if (ret == NOTIFY_BAD) if (ret & NOTIFY_STOP_MASK)
fail_clk = clk; fail_clk = clk;
} }
@ -1048,7 +1213,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
int ret = 0; int ret = 0;
/* prevent racing with updates to the clock topology */ /* prevent racing with updates to the clock topology */
mutex_lock(&prepare_lock); clk_prepare_lock();
/* bail early if nothing to do */ /* bail early if nothing to do */
if (rate == clk->rate) if (rate == clk->rate)
@ -1080,7 +1245,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
clk_change_rate(top); clk_change_rate(top);
out: out:
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -1096,9 +1261,9 @@ struct clk *clk_get_parent(struct clk *clk)
{ {
struct clk *parent; struct clk *parent;
mutex_lock(&prepare_lock); clk_prepare_lock();
parent = __clk_get_parent(clk); parent = __clk_get_parent(clk);
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return parent; return parent;
} }
@ -1162,16 +1327,8 @@ out:
return ret; return ret;
} }
void __clk_reparent(struct clk *clk, struct clk *new_parent) static void clk_reparent(struct clk *clk, struct clk *new_parent)
{ {
#ifdef CONFIG_COMMON_CLK_DEBUG
struct dentry *d;
struct dentry *new_parent_d;
#endif
if (!clk || !new_parent)
return;
hlist_del(&clk->child_node); hlist_del(&clk->child_node);
if (new_parent) if (new_parent)
@ -1179,39 +1336,20 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
else else
hlist_add_head(&clk->child_node, &clk_orphan_list); hlist_add_head(&clk->child_node, &clk_orphan_list);
#ifdef CONFIG_COMMON_CLK_DEBUG
if (!inited)
goto out;
if (new_parent)
new_parent_d = new_parent->dentry;
else
new_parent_d = orphandir;
d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
new_parent_d, clk->name);
if (d)
clk->dentry = d;
else
pr_debug("%s: failed to rename debugfs entry for %s\n",
__func__, clk->name);
out:
#endif
clk->parent = new_parent; clk->parent = new_parent;
}
void __clk_reparent(struct clk *clk, struct clk *new_parent)
{
clk_reparent(clk, new_parent);
clk_debug_reparent(clk, new_parent);
__clk_recalc_rates(clk, POST_RATE_CHANGE); __clk_recalc_rates(clk, POST_RATE_CHANGE);
} }
static int __clk_set_parent(struct clk *clk, struct clk *parent) static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
{ {
struct clk *old_parent;
unsigned long flags;
int ret = -EINVAL;
u8 i; u8 i;
old_parent = clk->parent;
if (!clk->parents) if (!clk->parents)
clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
GFP_KERNEL); GFP_KERNEL);
@ -1231,36 +1369,79 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
} }
} }
if (i == clk->num_parents) { return i;
pr_debug("%s: clock %s is not a possible parent of clock %s\n", }
__func__, parent->name, clk->name);
goto out;
}
/* migrate prepare and enable */ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
{
unsigned long flags;
int ret = 0;
struct clk *old_parent = clk->parent;
bool migrated_enable = false;
/* migrate prepare */
if (clk->prepare_count) if (clk->prepare_count)
__clk_prepare(parent); __clk_prepare(parent);
/* FIXME replace with clk_is_enabled(clk) someday */ flags = clk_enable_lock();
spin_lock_irqsave(&enable_lock, flags);
if (clk->enable_count) /* migrate enable */
if (clk->enable_count) {
__clk_enable(parent); __clk_enable(parent);
spin_unlock_irqrestore(&enable_lock, flags); migrated_enable = true;
}
/* update the clk tree topology */
clk_reparent(clk, parent);
clk_enable_unlock(flags);
/* change clock input source */ /* change clock input source */
ret = clk->ops->set_parent(clk->hw, i); if (parent && clk->ops->set_parent)
ret = clk->ops->set_parent(clk->hw, p_index);
/* clean up old prepare and enable */ if (ret) {
spin_lock_irqsave(&enable_lock, flags); /*
if (clk->enable_count) * The error handling is tricky due to that we need to release
* the spinlock while issuing the .set_parent callback. This
* means the new parent might have been enabled/disabled in
* between, which must be considered when doing rollback.
*/
flags = clk_enable_lock();
clk_reparent(clk, old_parent);
if (migrated_enable && clk->enable_count) {
__clk_disable(parent);
} else if (migrated_enable && (clk->enable_count == 0)) {
__clk_disable(old_parent); __clk_disable(old_parent);
spin_unlock_irqrestore(&enable_lock, flags); } else if (!migrated_enable && clk->enable_count) {
__clk_disable(parent);
__clk_enable(old_parent);
}
clk_enable_unlock(flags);
if (clk->prepare_count)
__clk_unprepare(parent);
return ret;
}
/* clean up enable for old parent if migration was done */
if (migrated_enable) {
flags = clk_enable_lock();
__clk_disable(old_parent);
clk_enable_unlock(flags);
}
/* clean up prepare for old parent if migration was done */
if (clk->prepare_count) if (clk->prepare_count)
__clk_unprepare(old_parent); __clk_unprepare(old_parent);
out: /* update debugfs with new clk tree topology */
return ret; clk_debug_reparent(clk, parent);
return 0;
} }
/** /**
@ -1278,44 +1459,59 @@ out:
int clk_set_parent(struct clk *clk, struct clk *parent) int clk_set_parent(struct clk *clk, struct clk *parent)
{ {
int ret = 0; int ret = 0;
u8 p_index = 0;
unsigned long p_rate = 0;
if (!clk || !clk->ops) if (!clk || !clk->ops)
return -EINVAL; return -EINVAL;
if (!clk->ops->set_parent) /* verify ops for for multi-parent clks */
if ((clk->num_parents > 1) && (!clk->ops->set_parent))
return -ENOSYS; return -ENOSYS;
/* prevent racing with updates to the clock topology */ /* prevent racing with updates to the clock topology */
mutex_lock(&prepare_lock); clk_prepare_lock();
if (clk->parent == parent) if (clk->parent == parent)
goto out; goto out;
/* propagate PRE_RATE_CHANGE notifications */ /* check that we are allowed to re-parent if the clock is in use */
if (clk->notifier_count) if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
ret = __clk_speculate_rates(clk, parent->rate);
/* abort if a driver objects */
if (ret == NOTIFY_STOP)
goto out;
/* only re-parent if the clock is not in use */
if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
ret = -EBUSY; ret = -EBUSY;
else
ret = __clk_set_parent(clk, parent);
/* propagate ABORT_RATE_CHANGE if .set_parent failed */
if (ret) {
__clk_recalc_rates(clk, ABORT_RATE_CHANGE);
goto out; goto out;
} }
/* propagate rate recalculation downstream */ /* try finding the new parent index */
__clk_reparent(clk, parent); if (parent) {
p_index = clk_fetch_parent_index(clk, parent);
p_rate = parent->rate;
if (p_index == clk->num_parents) {
pr_debug("%s: clk %s can not be parent of clk %s\n",
__func__, parent->name, clk->name);
ret = -EINVAL;
goto out;
}
}
/* propagate PRE_RATE_CHANGE notifications */
if (clk->notifier_count)
ret = __clk_speculate_rates(clk, p_rate);
/* abort if a driver objects */
if (ret & NOTIFY_STOP_MASK)
goto out;
/* do the re-parent */
ret = __clk_set_parent(clk, parent, p_index);
/* propagate rate recalculation accordingly */
if (ret)
__clk_recalc_rates(clk, ABORT_RATE_CHANGE);
else
__clk_recalc_rates(clk, POST_RATE_CHANGE);
out: out:
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -1338,7 +1534,7 @@ int __clk_init(struct device *dev, struct clk *clk)
if (!clk) if (!clk)
return -EINVAL; return -EINVAL;
mutex_lock(&prepare_lock); clk_prepare_lock();
/* check to see if a clock with this name is already registered */ /* check to see if a clock with this name is already registered */
if (__clk_lookup(clk->name)) { if (__clk_lookup(clk->name)) {
@ -1462,7 +1658,7 @@ int __clk_init(struct device *dev, struct clk *clk)
clk_debug_register(clk); clk_debug_register(clk);
out: out:
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -1696,7 +1892,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
if (!clk || !nb) if (!clk || !nb)
return -EINVAL; return -EINVAL;
mutex_lock(&prepare_lock); clk_prepare_lock();
/* search the list of notifiers for this clk */ /* search the list of notifiers for this clk */
list_for_each_entry(cn, &clk_notifier_list, node) list_for_each_entry(cn, &clk_notifier_list, node)
@ -1720,7 +1916,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
clk->notifier_count++; clk->notifier_count++;
out: out:
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }
@ -1745,7 +1941,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
if (!clk || !nb) if (!clk || !nb)
return -EINVAL; return -EINVAL;
mutex_lock(&prepare_lock); clk_prepare_lock();
list_for_each_entry(cn, &clk_notifier_list, node) list_for_each_entry(cn, &clk_notifier_list, node)
if (cn->clk == clk) if (cn->clk == clk)
@ -1766,7 +1962,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
ret = -ENOENT; ret = -ENOENT;
} }
mutex_unlock(&prepare_lock); clk_prepare_unlock();
return ret; return ret;
} }

View File

@ -156,7 +156,7 @@ static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
SARL_A370_PCLK_FREQ_OPT_MASK); SARL_A370_PCLK_FREQ_OPT_MASK);
if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) { if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) {
pr_err("CPU freq select unsuported %d\n", cpu_freq_select); pr_err("CPU freq select unsuported %d\n", cpu_freq_select);
cpu_freq = 0; cpu_freq = 0;
} else } else
@ -278,7 +278,7 @@ static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
SARH_AXP_PCLK_FREQ_OPT_MASK) SARH_AXP_PCLK_FREQ_OPT_MASK)
<< SARH_AXP_PCLK_FREQ_OPT_SHIFT); << SARH_AXP_PCLK_FREQ_OPT_SHIFT);
if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) { if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) {
pr_err("CPU freq select unsuported: %d\n", cpu_freq_select); pr_err("CPU freq select unsuported: %d\n", cpu_freq_select);
cpu_freq = 0; cpu_freq = 0;
} else } else

View File

@ -16,7 +16,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "clk-cpu.h"
#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
@ -173,17 +172,5 @@ clks_out:
kfree(cpuclk); kfree(cpuclk);
} }
static const __initconst struct of_device_id clk_cpu_match[] = { CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
{ of_cpu_clk_setup);
.compatible = "marvell,armada-xp-cpu-clock",
.data = of_cpu_clk_setup,
},
{
/* sentinel */
},
};
void __init mvebu_cpu_clk_init(void)
{
of_clk_init(clk_cpu_match);
}

View File

@ -1,22 +0,0 @@
/*
* Marvell MVEBU CPU clock handling.
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MVEBU_CLK_CPU_H
#define __MVEBU_CLK_CPU_H
#ifdef CONFIG_MVEBU_CLK_CPU
void __init mvebu_cpu_clk_init(void);
#else
static inline void mvebu_cpu_clk_init(void) {}
#endif
#endif

View File

@ -10,18 +10,14 @@
* warranty of any kind, whether express or implied. * warranty of any kind, whether express or implied.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/clk/mvebu.h>
#include <linux/of.h> #include <linux/of.h>
#include "clk-core.h" #include "clk-core.h"
#include "clk-cpu.h"
#include "clk-gating-ctrl.h" #include "clk-gating-ctrl.h"
void __init mvebu_clocks_init(void) void __init mvebu_clocks_init(void)
{ {
mvebu_core_clk_init(); mvebu_core_clk_init();
mvebu_gating_clk_init(); mvebu_gating_clk_init();
mvebu_cpu_clk_init(); of_clk_init(NULL);
} }

View File

@ -13,6 +13,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include "clk.h"
DEFINE_SPINLOCK(mxs_lock); DEFINE_SPINLOCK(mxs_lock);

View File

@ -960,47 +960,47 @@ void __init spear1340_clk_init(void)
SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock); SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);
clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); clk_register_clkdev(clk, NULL, "d0100000.spdif-in");
clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,
SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "acp_clk"); clk_register_clkdev(clk, NULL, "acp_clk");
clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "e2800000.gpio"); clk_register_clkdev(clk, NULL, "e2800000.gpio");
clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,
0, &_lock); 0, &_lock);
clk_register_clkdev(clk, NULL, "video_dec"); clk_register_clkdev(clk, NULL, "video_dec");
clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,
0, &_lock); 0, &_lock);
clk_register_clkdev(clk, NULL, "video_enc"); clk_register_clkdev(clk, NULL, "video_enc");
clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "spear_vip"); clk_register_clkdev(clk, NULL, "spear_vip");
clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "d0200000.cam0"); clk_register_clkdev(clk, NULL, "d0200000.cam0");
clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "d0300000.cam1"); clk_register_clkdev(clk, NULL, "d0300000.cam1");
clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "d0400000.cam2"); clk_register_clkdev(clk, NULL, "d0400000.cam2");
clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,
&_lock); &_lock);
clk_register_clkdev(clk, NULL, "d0500000.cam3"); clk_register_clkdev(clk, NULL, "d0500000.cam3");

View File

@ -0,0 +1,5 @@
#
# Makefile for sunxi specific clk
#
obj-y += clk-sunxi.o clk-factors.o

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2013 Emilio López <emilio@elopez.com.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Adjustable factor-based clock implementation
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/delay.h>
#include "clk-factors.h"
/*
* DOC: basic adjustable factor-based clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable.
* clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
* parent - fixed parent. No clk_set_parent support
*/
struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
struct clk_factors_config *config;
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
spinlock_t *lock;
};
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
#define FACTOR_SET(bit, len, reg, val) \
(((reg) & CLRMASK(len, bit)) | (val << (bit)))
static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u8 n = 1, k = 0, p = 0, m = 0;
u32 reg;
unsigned long rate;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
/* Fetch the register value */
reg = readl(factors->reg);
/* Get each individual factor if applicable */
if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
n = FACTOR_GET(config->nshift, config->nwidth, reg);
if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
k = FACTOR_GET(config->kshift, config->kwidth, reg);
if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
m = FACTOR_GET(config->mshift, config->mwidth, reg);
if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
p = FACTOR_GET(config->pshift, config->pwidth, reg);
/* Calculate the rate */
rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
return rate;
}
static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_factors *factors = to_clk_factors(hw);
factors->get_factors((u32 *)&rate, (u32)*parent_rate,
NULL, NULL, NULL, NULL);
return rate;
}
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
u8 n, k, m, p;
u32 reg;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
unsigned long flags = 0;
factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
if (factors->lock)
spin_lock_irqsave(factors->lock, flags);
/* Fetch the register value */
reg = readl(factors->reg);
/* Set up the new factors - macros do not do anything if width is 0 */
reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
/* Apply them now */
writel(reg, factors->reg);
/* delay 500us so pll stabilizes */
__delay((rate >> 20) * 500 / 2);
if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);
return 0;
}
static const struct clk_ops clk_factors_ops = {
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
};
/**
* clk_register_factors - register a factors clock with
* the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust factors
* @config: shift and width of factors n, k, m and p
* @get_factors: function to calculate the factors for a given frequency
* @lock: shared register lock for this clock
*/
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors)(u32 *rate, u32 parent,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock)
{
struct clk_factors *factors;
struct clk *clk;
struct clk_init_data init;
/* allocate the factors */
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors) {
pr_err("%s: could not allocate factors clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_factors_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_factors assignments */
factors->reg = reg;
factors->config = config;
factors->lock = lock;
factors->hw.init = &init;
factors->get_factors = get_factors;
/* register the clock */
clk = clk_register(dev, &factors->hw);
if (IS_ERR(clk))
kfree(factors);
return clk;
}

View File

@ -0,0 +1,27 @@
#ifndef __MACH_SUNXI_CLK_FACTORS_H
#define __MACH_SUNXI_CLK_FACTORS_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
struct clk_factors_config {
u8 nshift;
u8 nwidth;
u8 kshift;
u8 kwidth;
u8 mshift;
u8 mwidth;
u8 pshift;
u8 pwidth;
};
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors) (u32 *rate, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock);
#endif

View File

@ -0,0 +1,469 @@
/*
* Copyright 2013 Emilio López
*
* Emilio López <emilio@elopez.com.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/sunxi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock);
/**
* sunxi_osc_clk_setup() - Setup function for gatable oscillator
*/
#define SUNXI_OSC24M_GATE 0
static void __init sunxi_osc_clk_setup(struct device_node *node)
{
struct clk *clk;
struct clk_fixed_rate *fixed;
struct clk_gate *gate;
const char *clk_name = node->name;
u32 rate;
/* allocate fixed-rate and gate clock structs */
fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
if (!fixed)
return;
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
if (!gate) {
kfree(fixed);
return;
}
if (of_property_read_u32(node, "clock-frequency", &rate))
return;
/* set up gate and fixed rate properties */
gate->reg = of_iomap(node, 0);
gate->bit_idx = SUNXI_OSC24M_GATE;
gate->lock = &clk_lock;
fixed->fixed_rate = rate;
clk = clk_register_composite(NULL, clk_name,
NULL, 0,
NULL, NULL,
&fixed->hw, &clk_fixed_rate_ops,
&gate->hw, &clk_gate_ops,
CLK_IS_ROOT);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
* PLL1 rate is calculated as follows
* rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
* parent_rate is always 24Mhz
*/
static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 div;
/* Normalize value to a 6M multiple */
div = *freq / 6000000;
*freq = 6000000 * div;
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
/* m is always zero for pll1 */
*m = 0;
/* k is 1 only on these cases */
if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
*k = 1;
else
*k = 0;
/* p will be 3 for divs under 10 */
if (div < 10)
*p = 3;
/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
else if (div < 20 || (div < 32 && (div & 1)))
*p = 2;
/* p will be 1 for even divs under 32, divs under 40 and odd pairs
* of divs between 40-62 */
else if (div < 40 || (div < 64 && (div & 2)))
*p = 1;
/* any other entries have p = 0 */
else
*p = 0;
/* calculate a suitable n based on k and p */
div <<= *p;
div /= (*k + 1);
*n = div / 4;
}
/**
* sunxi_get_apb1_factors() - calculates m, p factors for APB1
* APB1 rate is calculated as follows
* rate = (parent_rate >> p) / (m + 1);
*/
static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 calcm, calcp;
if (parent_rate < *freq)
*freq = parent_rate;
parent_rate = (parent_rate + (*freq - 1)) / *freq;
/* Invalid rate! */
if (parent_rate > 32)
return;
if (parent_rate <= 4)
calcp = 0;
else if (parent_rate <= 8)
calcp = 1;
else if (parent_rate <= 16)
calcp = 2;
else
calcp = 3;
calcm = (parent_rate >> calcp) - 1;
*freq = (parent_rate >> calcp) / (calcm + 1);
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
*m = calcm;
*p = calcp;
}
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
struct factors_data {
struct clk_factors_config *table;
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
};
static struct clk_factors_config pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
.kwidth = 2,
.mshift = 0,
.mwidth = 2,
.pshift = 16,
.pwidth = 2,
};
static struct clk_factors_config apb1_config = {
.mshift = 0,
.mwidth = 5,
.pshift = 16,
.pwidth = 2,
};
static const __initconst struct factors_data pll1_data = {
.table = &pll1_config,
.getter = sunxi_get_pll1_factors,
};
static const __initconst struct factors_data apb1_data = {
.table = &apb1_config,
.getter = sunxi_get_apb1_factors,
};
static void __init sunxi_factors_clk_setup(struct device_node *node,
struct factors_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent;
void *reg;
reg = of_iomap(node, 0);
parent = of_clk_get_parent_name(node, 0);
clk = clk_register_factors(NULL, clk_name, parent, 0, reg,
data->table, data->getter, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_mux_clk_setup() - Setup function for muxes
*/
#define SUNXI_MUX_GATE_WIDTH 2
struct mux_data {
u8 shift;
};
static const __initconst struct mux_data cpu_data = {
.shift = 16,
};
static const __initconst struct mux_data apb1_mux_data = {
.shift = 24,
};
static void __init sunxi_mux_clk_setup(struct device_node *node,
struct mux_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parents[5];
void *reg;
int i = 0;
reg = of_iomap(node, 0);
while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_divider_clk_setup() - Setup function for simple divider clocks
*/
#define SUNXI_DIVISOR_WIDTH 2
struct div_data {
u8 shift;
u8 pow;
};
static const __initconst struct div_data axi_data = {
.shift = 0,
.pow = 0,
};
static const __initconst struct div_data ahb_data = {
.shift = 4,
.pow = 1,
};
static const __initconst struct div_data apb0_data = {
.shift = 8,
.pow = 1,
};
static void __init sunxi_divider_clk_setup(struct device_node *node,
struct div_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *clk_parent;
void *reg;
reg = of_iomap(node, 0);
clk_parent = of_clk_get_parent_name(node, 0);
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
reg, data->shift, SUNXI_DIVISOR_WIDTH,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
&clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
*/
#define SUNXI_GATES_MAX_SIZE 64
struct gates_data {
DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
};
static const __initconst struct gates_data axi_gates_data = {
.mask = {1},
};
static const __initconst struct gates_data ahb_gates_data = {
.mask = {0x7F77FFF, 0x14FB3F},
};
static const __initconst struct gates_data apb0_gates_data = {
.mask = {0x4EF},
};
static const __initconst struct gates_data apb1_gates_data = {
.mask = {0xFF00F7},
};
static void __init sunxi_gates_clk_setup(struct device_node *node,
struct gates_data *data)
{
struct clk_onecell_data *clk_data;
const char *clk_parent;
const char *clk_name;
void *reg;
int qty;
int i = 0;
int j = 0;
int ignore;
reg = of_iomap(node, 0);
clk_parent = of_clk_get_parent_name(node, 0);
/* Worst-case size approximation and memory allocation */
qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE);
clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
if (!clk_data)
return;
clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks) {
kfree(clk_data);
return;
}
for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) {
of_property_read_string_index(node, "clock-output-names",
j, &clk_name);
/* No driver claims this clock, but it should remain gated */
ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0;
clk_data->clks[i] = clk_register_gate(NULL, clk_name,
clk_parent, ignore,
reg + 4 * (i/32), i % 32,
0, &clk_lock);
WARN_ON(IS_ERR(clk_data->clks[i]));
j++;
}
/* Adjust to the real max */
clk_data->clk_num = i;
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
}
/* Matches for of_clk_init */
static const __initconst struct of_device_id clk_match[] = {
{.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
{}
};
/* Matches for factors clocks */
static const __initconst struct of_device_id clk_factors_match[] = {
{.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,},
{.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,},
{}
};
/* Matches for divider clocks */
static const __initconst struct of_device_id clk_div_match[] = {
{.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,},
{.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,},
{.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,},
{}
};
/* Matches for mux clocks */
static const __initconst struct of_device_id clk_mux_match[] = {
{.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,},
{.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,},
{}
};
/* Matches for gate clocks */
static const __initconst struct of_device_id clk_gates_match[] = {
{.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,},
{.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,},
{.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,},
{.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,},
{}
};
static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
void *function)
{
struct device_node *np;
const struct div_data *data;
const struct of_device_id *match;
void (*setup_function)(struct device_node *, const void *) = function;
for_each_matching_node(np, clk_match) {
match = of_match_node(clk_match, np);
data = match->data;
setup_function(np, data);
}
}
void __init sunxi_init_clocks(void)
{
/* Register all the simple sunxi clocks on DT */
of_clk_init(clk_match);
/* Register factor clocks */
of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
/* Register divider clocks */
of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
/* Register mux clocks */
of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
/* Register gate clocks */
of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
}

View File

@ -355,15 +355,16 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
struct tegra_clk_periph *periph, void __iomem *clk_base, struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset); u32 offset);
#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ #define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \
_div_shift, _div_width, _div_frac_width, \ _div_shift, _div_width, _div_frac_width, \
_div_flags, _clk_num, _enb_refcnt, _regs, \ _div_flags, _clk_num, _enb_refcnt, _regs, \
_gate_flags) \ _gate_flags, _table) \
{ \ { \
.mux = { \ .mux = { \
.flags = _mux_flags, \ .flags = _mux_flags, \
.shift = _mux_shift, \ .shift = _mux_shift, \
.width = _mux_width, \ .mask = _mux_mask, \
.table = _table, \
}, \ }, \
.divider = { \ .divider = { \
.flags = _div_flags, \ .flags = _div_flags, \
@ -393,26 +394,36 @@ struct tegra_periph_init_data {
const char *dev_id; const char *dev_id;
}; };
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ #define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_width, _mux_flags, _div_shift, \ _mux_shift, _mux_mask, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \ _div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \ _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \
{ \ { \
.name = _name, \ .name = _name, \
.clk_id = _clk_id, \ .clk_id = _clk_id, \
.parent_names = _parent_names, \ .parent_names = _parent_names, \
.num_parents = ARRAY_SIZE(_parent_names), \ .num_parents = ARRAY_SIZE(_parent_names), \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \
_mux_flags, _div_shift, \ _mux_flags, _div_shift, \
_div_width, _div_frac_width, \ _div_width, _div_frac_width, \
_div_flags, _clk_num, \ _div_flags, _clk_num, \
_enb_refcnt, _regs, \ _enb_refcnt, _regs, \
_gate_flags), \ _gate_flags, _table), \
.offset = _offset, \ .offset = _offset, \
.con_id = _con_id, \ .con_id = _con_id, \
.dev_id = _dev_id, \ .dev_id = _dev_id, \
} }
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_width, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, BIT(_mux_width) - 1, _mux_flags, \
_div_shift, _div_width, _div_frac_width, _div_flags, \
_regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\
NULL)
/** /**
* struct clk_super_mux - super clock * struct clk_super_mux - super clock
* *

View File

@ -5,6 +5,7 @@
# Clock types # Clock types
obj-y += clk-prcc.o obj-y += clk-prcc.o
obj-y += clk-prcmu.o obj-y += clk-prcmu.o
obj-y += clk-sysctrl.o
# Clock definitions # Clock definitions
obj-y += u8500_clk.o obj-y += u8500_clk.o

View File

@ -12,13 +12,78 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
/* TODO: Add clock implementations here */ #include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/mfd/dbx500-prcmu.h>
#include "clk.h"
/* Clock definitions for ab8500 */ /* Clock definitions for ab8500 */
static int ab8500_reg_clks(struct device *dev) static int ab8500_reg_clks(struct device *dev)
{ {
int ret;
struct clk *clk;
const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"};
u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1};
u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK};
u8 intclk_reg_bits[] = {
0 ,
(1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT)
};
dev_info(dev, "register clocks for ab850x\n");
/* Enable SWAT */
ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
if (ret)
return ret;
/* ab8500_sysclk */
clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK,
CLK_IS_ROOT);
clk_register_clkdev(clk, "sysclk", "ab8500-usb.0");
clk_register_clkdev(clk, "sysclk", "ab-iddet.0");
clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0");
clk_register_clkdev(clk, "sysclk", "shrm_bus");
/* ab8500_sysclk2 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0);
clk_register_clkdev(clk, "sysclk", "0-0070");
/* ab8500_sysclk3 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0);
clk_register_clkdev(clk, "sysclk", "cg1960_core.0");
/* ab8500_sysclk4 */
clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0);
/* ab_ulpclk */
clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL,
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
38400000, 9000, CLK_IS_ROOT);
clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0");
/* ab8500_intclk */
clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2,
intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0);
clk_register_clkdev(clk, "intclk", "ab85xx-codec.0");
clk_register_clkdev(clk, NULL, "ab8500-pwm.1");
/* ab8500_audioclk */
clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk",
AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA,
AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0);
clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0");
return 0; return 0;
} }

View File

@ -20,15 +20,23 @@
struct clk_prcmu { struct clk_prcmu {
struct clk_hw hw; struct clk_hw hw;
u8 cg_sel; u8 cg_sel;
int is_prepared;
int is_enabled; int is_enabled;
int opp_requested;
}; };
/* PRCMU clock operations. */ /* PRCMU clock operations. */
static int clk_prcmu_prepare(struct clk_hw *hw) static int clk_prcmu_prepare(struct clk_hw *hw)
{ {
int ret;
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
return prcmu_request_clock(clk->cg_sel, true);
ret = prcmu_request_clock(clk->cg_sel, true);
if (!ret)
clk->is_prepared = 1;
return ret;;
} }
static void clk_prcmu_unprepare(struct clk_hw *hw) static void clk_prcmu_unprepare(struct clk_hw *hw)
@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw)
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false)) if (prcmu_request_clock(clk->cg_sel, false))
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name); __clk_get_name(hw->clk));
else
clk->is_prepared = 0;
}
static int clk_prcmu_is_prepared(struct clk_hw *hw)
{
struct clk_prcmu *clk = to_clk_prcmu(hw);
return clk->is_prepared;
} }
static int clk_prcmu_enable(struct clk_hw *hw) static int clk_prcmu_enable(struct clk_hw *hw)
@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
return prcmu_set_clock_rate(clk->cg_sel, rate); return prcmu_set_clock_rate(clk->cg_sel, rate);
} }
static int request_ape_opp100(bool enable)
{
static int reqs;
int err = 0;
if (enable) {
if (!reqs)
err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
"clock", 100);
if (!err)
reqs++;
} else {
reqs--;
if (!reqs)
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
"clock");
}
return err;
}
static int clk_prcmu_opp_prepare(struct clk_hw *hw) static int clk_prcmu_opp_prepare(struct clk_hw *hw)
{ {
int err; int err;
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
err = request_ape_opp100(true); if (!clk->opp_requested) {
err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk),
100);
if (err) { if (err) {
pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
__func__, hw->init->name); __func__, __clk_get_name(hw->clk));
return err; return err;
} }
clk->opp_requested = 1;
}
err = prcmu_request_clock(clk->cg_sel, true); err = prcmu_request_clock(clk->cg_sel, true);
if (err) if (err) {
request_ape_opp100(false); prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk));
clk->opp_requested = 0;
return err; return err;
}
clk->is_prepared = 1;
return 0;
} }
static void clk_prcmu_opp_unprepare(struct clk_hw *hw) static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
{ {
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false)) if (prcmu_request_clock(clk->cg_sel, false)) {
goto out_error;
if (request_ape_opp100(false))
goto out_error;
return;
out_error:
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name); __clk_get_name(hw->clk));
return;
}
if (clk->opp_requested) {
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk));
clk->opp_requested = 0;
}
clk->is_prepared = 0;
} }
static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
int err; int err;
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
if (!clk->opp_requested) {
err = prcmu_request_ape_opp_100_voltage(true); err = prcmu_request_ape_opp_100_voltage(true);
if (err) { if (err) {
pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
__func__, hw->init->name); __func__, __clk_get_name(hw->clk));
return err; return err;
} }
clk->opp_requested = 1;
}
err = prcmu_request_clock(clk->cg_sel, true); err = prcmu_request_clock(clk->cg_sel, true);
if (err) if (err) {
prcmu_request_ape_opp_100_voltage(false); prcmu_request_ape_opp_100_voltage(false);
clk->opp_requested = 0;
return err; return err;
}
clk->is_prepared = 1;
return 0;
} }
static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
{ {
struct clk_prcmu *clk = to_clk_prcmu(hw); struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false)) if (prcmu_request_clock(clk->cg_sel, false)) {
goto out_error;
if (prcmu_request_ape_opp_100_voltage(false))
goto out_error;
return;
out_error:
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name); __clk_get_name(hw->clk));
return;
}
if (clk->opp_requested) {
prcmu_request_ape_opp_100_voltage(false);
clk->opp_requested = 0;
}
clk->is_prepared = 0;
} }
static struct clk_ops clk_prcmu_scalable_ops = { static struct clk_ops clk_prcmu_scalable_ops = {
.prepare = clk_prcmu_prepare, .prepare = clk_prcmu_prepare,
.unprepare = clk_prcmu_unprepare, .unprepare = clk_prcmu_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable, .enable = clk_prcmu_enable,
.disable = clk_prcmu_disable, .disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled, .is_enabled = clk_prcmu_is_enabled,
@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = {
static struct clk_ops clk_prcmu_gate_ops = { static struct clk_ops clk_prcmu_gate_ops = {
.prepare = clk_prcmu_prepare, .prepare = clk_prcmu_prepare,
.unprepare = clk_prcmu_unprepare, .unprepare = clk_prcmu_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable, .enable = clk_prcmu_enable,
.disable = clk_prcmu_disable, .disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled, .is_enabled = clk_prcmu_is_enabled,
@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = {
static struct clk_ops clk_prcmu_opp_gate_ops = { static struct clk_ops clk_prcmu_opp_gate_ops = {
.prepare = clk_prcmu_opp_prepare, .prepare = clk_prcmu_opp_prepare,
.unprepare = clk_prcmu_opp_unprepare, .unprepare = clk_prcmu_opp_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable, .enable = clk_prcmu_enable,
.disable = clk_prcmu_disable, .disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled, .is_enabled = clk_prcmu_is_enabled,
@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = {
static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
.prepare = clk_prcmu_opp_volt_prepare, .prepare = clk_prcmu_opp_volt_prepare,
.unprepare = clk_prcmu_opp_volt_unprepare, .unprepare = clk_prcmu_opp_volt_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable, .enable = clk_prcmu_enable,
.disable = clk_prcmu_disable, .disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled, .is_enabled = clk_prcmu_is_enabled,
@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name,
} }
clk->cg_sel = cg_sel; clk->cg_sel = cg_sel;
clk->is_prepared = 1;
clk->is_enabled = 1; clk->is_enabled = 1;
clk->opp_requested = 0;
/* "rate" can be used for changing the initial frequency */ /* "rate" can be used for changing the initial frequency */
if (rate) if (rate)
prcmu_set_clock_rate(cg_sel, rate); prcmu_set_clock_rate(cg_sel, rate);

View File

@ -0,0 +1,221 @@
/*
* Sysctrl clock implementation for ux500 platform.
*
* Copyright (C) 2013 ST-Ericsson SA
* Author: Ulf Hansson <ulf.hansson@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#include <linux/clk-provider.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/err.h>
#include "clk.h"
#define SYSCTRL_MAX_NUM_PARENTS 4
#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw)
struct clk_sysctrl {
struct clk_hw hw;
struct device *dev;
u8 parent_index;
u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS];
u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS];
u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS];
unsigned long rate;
unsigned long enable_delay_us;
};
/* Sysctrl clock operations. */
static int clk_sysctrl_prepare(struct clk_hw *hw)
{
int ret;
struct clk_sysctrl *clk = to_clk_sysctrl(hw);
ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0],
clk->reg_bits[0]);
if (!ret && clk->enable_delay_us)
usleep_range(clk->enable_delay_us, clk->enable_delay_us);
return ret;
}
static void clk_sysctrl_unprepare(struct clk_hw *hw)
{
struct clk_sysctrl *clk = to_clk_sysctrl(hw);
if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0]))
dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n",
__func__, __clk_get_name(hw->clk));
}
static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_sysctrl *clk = to_clk_sysctrl(hw);
return clk->rate;
}
static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_sysctrl *clk = to_clk_sysctrl(hw);
u8 old_index = clk->parent_index;
int ret = 0;
if (clk->reg_sel[old_index]) {
ret = ab8500_sysctrl_clear(clk->reg_sel[old_index],
clk->reg_mask[old_index]);
if (ret)
return ret;
}
if (clk->reg_sel[index]) {
ret = ab8500_sysctrl_write(clk->reg_sel[index],
clk->reg_mask[index],
clk->reg_bits[index]);
if (ret) {
if (clk->reg_sel[old_index])
ab8500_sysctrl_write(clk->reg_sel[old_index],
clk->reg_mask[old_index],
clk->reg_bits[old_index]);
return ret;
}
}
clk->parent_index = index;
return ret;
}
static u8 clk_sysctrl_get_parent(struct clk_hw *hw)
{
struct clk_sysctrl *clk = to_clk_sysctrl(hw);
return clk->parent_index;
}
static struct clk_ops clk_sysctrl_gate_ops = {
.prepare = clk_sysctrl_prepare,
.unprepare = clk_sysctrl_unprepare,
};
static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
.prepare = clk_sysctrl_prepare,
.unprepare = clk_sysctrl_unprepare,
.recalc_rate = clk_sysctrl_recalc_rate,
};
static struct clk_ops clk_sysctrl_set_parent_ops = {
.set_parent = clk_sysctrl_set_parent,
.get_parent = clk_sysctrl_get_parent,
};
static struct clk *clk_reg_sysctrl(struct device *dev,
const char *name,
const char **parent_names,
u8 num_parents,
u16 *reg_sel,
u8 *reg_mask,
u8 *reg_bits,
unsigned long rate,
unsigned long enable_delay_us,
unsigned long flags,
struct clk_ops *clk_sysctrl_ops)
{
struct clk_sysctrl *clk;
struct clk_init_data clk_sysctrl_init;
struct clk *clk_reg;
int i;
if (!dev)
return ERR_PTR(-EINVAL);
if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) {
dev_err(dev, "clk_sysctrl: invalid arguments passed\n");
return ERR_PTR(-EINVAL);
}
clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL);
if (!clk) {
dev_err(dev, "clk_sysctrl: could not allocate clk\n");
return ERR_PTR(-ENOMEM);
}
for (i = 0; i < num_parents; i++) {
clk->reg_sel[i] = reg_sel[i];
clk->reg_bits[i] = reg_bits[i];
clk->reg_mask[i] = reg_mask[i];
}
clk->parent_index = 0;
clk->rate = rate;
clk->enable_delay_us = enable_delay_us;
clk->dev = dev;
clk_sysctrl_init.name = name;
clk_sysctrl_init.ops = clk_sysctrl_ops;
clk_sysctrl_init.flags = flags;
clk_sysctrl_init.parent_names = parent_names;
clk_sysctrl_init.num_parents = num_parents;
clk->hw.init = &clk_sysctrl_init;
clk_reg = devm_clk_register(clk->dev, &clk->hw);
if (IS_ERR(clk_reg))
dev_err(dev, "clk_sysctrl: clk_register failed\n");
return clk_reg;
}
struct clk *clk_reg_sysctrl_gate(struct device *dev,
const char *name,
const char *parent_name,
u16 reg_sel,
u8 reg_mask,
u8 reg_bits,
unsigned long enable_delay_us,
unsigned long flags)
{
const char **parent_names = (parent_name ? &parent_name : NULL);
u8 num_parents = (parent_name ? 1 : 0);
return clk_reg_sysctrl(dev, name, parent_names, num_parents,
&reg_sel, &reg_mask, &reg_bits, 0, enable_delay_us,
flags, &clk_sysctrl_gate_ops);
}
struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
const char *name,
const char *parent_name,
u16 reg_sel,
u8 reg_mask,
u8 reg_bits,
unsigned long rate,
unsigned long enable_delay_us,
unsigned long flags)
{
const char **parent_names = (parent_name ? &parent_name : NULL);
u8 num_parents = (parent_name ? 1 : 0);
return clk_reg_sysctrl(dev, name, parent_names, num_parents,
&reg_sel, &reg_mask, &reg_bits,
rate, enable_delay_us, flags,
&clk_sysctrl_gate_fixed_rate_ops);
}
struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
const char *name,
const char **parent_names,
u8 num_parents,
u16 *reg_sel,
u8 *reg_mask,
u8 *reg_bits,
unsigned long flags)
{
return clk_reg_sysctrl(dev, name, parent_names, num_parents,
reg_sel, reg_mask, reg_bits, 0, 0, flags,
&clk_sysctrl_set_parent_ops);
}

View File

@ -11,16 +11,18 @@
#define __UX500_CLK_H #define __UX500_CLK_H
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h>
#include <linux/types.h>
struct clk *clk_reg_prcc_pclk(const char *name, struct clk *clk_reg_prcc_pclk(const char *name,
const char *parent_name, const char *parent_name,
unsigned int phy_base, resource_size_t phy_base,
u32 cg_sel, u32 cg_sel,
unsigned long flags); unsigned long flags);
struct clk *clk_reg_prcc_kclk(const char *name, struct clk *clk_reg_prcc_kclk(const char *name,
const char *parent_name, const char *parent_name,
unsigned int phy_base, resource_size_t phy_base,
u32 cg_sel, u32 cg_sel,
unsigned long flags); unsigned long flags);
@ -57,4 +59,32 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
unsigned long rate, unsigned long rate,
unsigned long flags); unsigned long flags);
struct clk *clk_reg_sysctrl_gate(struct device *dev,
const char *name,
const char *parent_name,
u16 reg_sel,
u8 reg_mask,
u8 reg_bits,
unsigned long enable_delay_us,
unsigned long flags);
struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
const char *name,
const char *parent_name,
u16 reg_sel,
u8 reg_mask,
u8 reg_bits,
unsigned long rate,
unsigned long enable_delay_us,
unsigned long flags);
struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
const char *name,
const char **parent_names,
u8 num_parents,
u16 *reg_sel,
u8 *reg_mask,
u8 *reg_bits,
unsigned long flags);
#endif /* __UX500_CLK_H */ #endif /* __UX500_CLK_H */

View File

@ -3,5 +3,5 @@ obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o
obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o

View File

@ -0,0 +1,188 @@
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2013 ARM Limited
*/
#include <linux/amba/sp810.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define to_clk_sp810_timerclken(_hw) \
container_of(_hw, struct clk_sp810_timerclken, hw)
struct clk_sp810;
struct clk_sp810_timerclken {
struct clk_hw hw;
struct clk *clk;
struct clk_sp810 *sp810;
int channel;
};
struct clk_sp810 {
struct device_node *node;
int refclk_index, timclk_index;
void __iomem *base;
spinlock_t lock;
struct clk_sp810_timerclken timerclken[4];
struct clk *refclk;
struct clk *timclk;
};
static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
{
struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
u32 val = readl(timerclken->sp810->base + SCCTRL);
return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
}
static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
struct clk_sp810 *sp810 = timerclken->sp810;
u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
unsigned long flags = 0;
if (WARN_ON(index > 1))
return -EINVAL;
spin_lock_irqsave(&sp810->lock, flags);
val = readl(sp810->base + SCCTRL);
val &= ~(1 << shift);
val |= index << shift;
writel(val, sp810->base + SCCTRL);
spin_unlock_irqrestore(&sp810->lock, flags);
return 0;
}
/*
* FIXME - setting the parent every time .prepare is invoked is inefficient.
* This is better handled by a dedicated clock tree configuration mechanism at
* init-time. Revisit this later when such a mechanism exists
*/
static int clk_sp810_timerclken_prepare(struct clk_hw *hw)
{
struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
struct clk_sp810 *sp810 = timerclken->sp810;
struct clk *old_parent = __clk_get_parent(hw->clk);
struct clk *new_parent;
if (!sp810->refclk)
sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index);
if (!sp810->timclk)
sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index);
if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk)))
return -ENOENT;
/* Select fastest parent */
if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk))
new_parent = sp810->refclk;
else
new_parent = sp810->timclk;
/* Switch the parent if necessary */
if (old_parent != new_parent) {
clk_prepare(new_parent);
clk_set_parent(hw->clk, new_parent);
clk_unprepare(old_parent);
}
return 0;
}
static void clk_sp810_timerclken_unprepare(struct clk_hw *hw)
{
struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
struct clk_sp810 *sp810 = timerclken->sp810;
clk_put(sp810->timclk);
clk_put(sp810->refclk);
}
static const struct clk_ops clk_sp810_timerclken_ops = {
.prepare = clk_sp810_timerclken_prepare,
.unprepare = clk_sp810_timerclken_unprepare,
.get_parent = clk_sp810_timerclken_get_parent,
.set_parent = clk_sp810_timerclken_set_parent,
};
struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
void *data)
{
struct clk_sp810 *sp810 = data;
if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
ARRAY_SIZE(sp810->timerclken)))
return NULL;
return sp810->timerclken[clkspec->args[0]].clk;
}
void __init clk_sp810_of_setup(struct device_node *node)
{
struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
const char *parent_names[2];
char name[12];
struct clk_init_data init;
int i;
if (!sp810) {
pr_err("Failed to allocate memory for SP810!\n");
return;
}
sp810->refclk_index = of_property_match_string(node, "clock-names",
"refclk");
parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index);
sp810->timclk_index = of_property_match_string(node, "clock-names",
"timclk");
parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index);
if (parent_names[0] <= 0 || parent_names[1] <= 0) {
pr_warn("Failed to obtain parent clocks for SP810!\n");
return;
}
sp810->node = node;
sp810->base = of_iomap(node, 0);
spin_lock_init(&sp810->lock);
init.name = name;
init.ops = &clk_sp810_timerclken_ops;
init.flags = CLK_IS_BASIC;
init.parent_names = parent_names;
init.num_parents = ARRAY_SIZE(parent_names);
for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
sp810->timerclken[i].sp810 = sp810;
sp810->timerclken[i].channel = i;
sp810->timerclken[i].hw.init = &init;
sp810->timerclken[i].clk = clk_register(NULL,
&sp810->timerclken[i].hw);
WARN_ON(IS_ERR(sp810->timerclken[i].clk));
}
of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
}
CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);

View File

@ -15,8 +15,6 @@
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/vexpress.h> #include <linux/vexpress.h>
static struct clk *vexpress_sp810_timerclken[4]; static struct clk *vexpress_sp810_timerclken[4];
@ -86,50 +84,3 @@ void __init vexpress_clk_init(void __iomem *sp810_base)
WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1], WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
"v2m-timer1", "sp804")); "v2m-timer1", "sp804"));
} }
#if defined(CONFIG_OF)
struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data)
{
if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
ARRAY_SIZE(vexpress_sp810_timerclken)))
return NULL;
return vexpress_sp810_timerclken[clkspec->args[0]];
}
void __init vexpress_clk_of_init(void)
{
struct device_node *node;
struct clk *clk;
struct clk *refclk, *timclk;
of_clk_init(NULL);
node = of_find_compatible_node(NULL, NULL, "arm,sp810");
vexpress_sp810_init(of_iomap(node, 0));
of_clk_add_provider(node, vexpress_sp810_of_get, NULL);
/* Select "better" (faster) parent for SP804 timers */
refclk = of_clk_get_by_name(node, "refclk");
timclk = of_clk_get_by_name(node, "timclk");
if (!WARN_ON(IS_ERR(refclk) || IS_ERR(timclk))) {
int i = 0;
if (clk_get_rate(refclk) > clk_get_rate(timclk))
clk = refclk;
else
clk = timclk;
for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i],
clk));
}
WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
"v2m-timer0", "sp804"));
WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
"v2m-timer1", "sp804"));
}
#endif

View File

@ -23,7 +23,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/sunxi_timer.h> #include <linux/sunxi_timer.h>
#include <linux/clk-provider.h> #include <linux/clk/sunxi.h>
#define TIMER_CTL_REG 0x00 #define TIMER_CTL_REG 0x00
#define TIMER_CTL_ENABLE (1 << 0) #define TIMER_CTL_ENABLE (1 << 0)
@ -123,7 +123,7 @@ void __init sunxi_timer_init(void)
if (irq <= 0) if (irq <= 0)
panic("Can't parse IRQ"); panic("Can't parse IRQ");
of_clk_init(NULL); sunxi_init_clocks();
clk = of_clk_get(node, 0); clk = of_clk_get(node, 0);
if (IS_ERR(clk)) if (IS_ERR(clk))

View File

@ -152,7 +152,7 @@ struct clk {
}, \ }, \
.reg = _reg, \ .reg = _reg, \
.shift = _shift, \ .shift = _shift, \
.width = _width, \ .mask = BIT(_width) - 1, \
.flags = _mux_flags, \ .flags = _mux_flags, \
.lock = _lock, \ .lock = _lock, \
}; \ }; \

View File

@ -45,6 +45,14 @@ struct clk_hw;
* undo any work done in the @prepare callback. Called with * undo any work done in the @prepare callback. Called with
* prepare_lock held. * prepare_lock held.
* *
* @is_prepared: Queries the hardware to determine if the clock is prepared.
* This function is allowed to sleep. Optional, if this op is not
* set then the prepare count will be used.
*
* @unprepare_unused: Unprepare the clock atomically. Only called from
* clk_disable_unused for prepare clocks with special needs.
* Called with prepare mutex held. This function may sleep.
*
* @enable: Enable the clock atomically. This must not return until the * @enable: Enable the clock atomically. This must not return until the
* clock is generating a valid clock signal, usable by consumer * clock is generating a valid clock signal, usable by consumer
* devices. Called with enable_lock held. This function must not * devices. Called with enable_lock held. This function must not
@ -108,6 +116,8 @@ struct clk_hw;
struct clk_ops { struct clk_ops {
int (*prepare)(struct clk_hw *hw); int (*prepare)(struct clk_hw *hw);
void (*unprepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw);
int (*is_prepared)(struct clk_hw *hw);
void (*unprepare_unused)(struct clk_hw *hw);
int (*enable)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw);
void (*disable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw);
int (*is_enabled)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw);
@ -239,9 +249,14 @@ struct clk_div_table {
* CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the
* register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is
* the raw value read from the register, with the value of zero considered * the raw value read from the register, with the value of zero considered
* invalid * invalid, unless CLK_DIVIDER_ALLOW_ZERO is set.
* CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from
* the hardware register * the hardware register
* CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have
* CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor.
* Some hardware implementations gracefully handle this case and allow a
* zero divisor by not modifying their input clock
* (divide by one / bypass).
*/ */
struct clk_divider { struct clk_divider {
struct clk_hw hw; struct clk_hw hw;
@ -255,6 +270,7 @@ struct clk_divider {
#define CLK_DIVIDER_ONE_BASED BIT(0) #define CLK_DIVIDER_ONE_BASED BIT(0)
#define CLK_DIVIDER_POWER_OF_TWO BIT(1) #define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
extern const struct clk_ops clk_divider_ops; extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name, struct clk *clk_register_divider(struct device *dev, const char *name,
@ -274,7 +290,7 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
* @reg: register controlling multiplexer * @reg: register controlling multiplexer
* @shift: shift to multiplexer bit field * @shift: shift to multiplexer bit field
* @width: width of mutliplexer bit field * @width: width of mutliplexer bit field
* @num_clks: number of parent clocks * @flags: hardware-specific flags
* @lock: register lock * @lock: register lock
* *
* Clock with multiple selectable parents. Implements .get_parent, .set_parent * Clock with multiple selectable parents. Implements .get_parent, .set_parent
@ -287,8 +303,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
struct clk_mux { struct clk_mux {
struct clk_hw hw; struct clk_hw hw;
void __iomem *reg; void __iomem *reg;
u32 *table;
u32 mask;
u8 shift; u8 shift;
u8 width;
u8 flags; u8 flags;
spinlock_t *lock; spinlock_t *lock;
}; };
@ -297,11 +314,19 @@ struct clk_mux {
#define CLK_MUX_INDEX_BIT BIT(1) #define CLK_MUX_INDEX_BIT BIT(1)
extern const struct clk_ops clk_mux_ops; extern const struct clk_ops clk_mux_ops;
struct clk *clk_register_mux(struct device *dev, const char *name, struct clk *clk_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags, const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width, void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock); u8 clk_mux_flags, spinlock_t *lock);
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table, spinlock_t *lock);
void of_fixed_factor_clk_setup(struct device_node *node);
/** /**
* struct clk_fixed_factor - fixed multiplier and divider clock * struct clk_fixed_factor - fixed multiplier and divider clock
* *
@ -325,6 +350,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
const char *parent_name, unsigned long flags, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div); unsigned int mult, unsigned int div);
/***
* struct clk_composite - aggregate clock of mux, divider and gate clocks
*
* @hw: handle between common and hardware-specific interfaces
* @mux_hw: handle between composite and hardware-specific mux clock
* @rate_hw: handle between composite and hardware-specific rate clock
* @gate_hw: handle between composite and hardware-specific gate clock
* @mux_ops: clock ops for mux
* @rate_ops: clock ops for rate
* @gate_ops: clock ops for gate
*/
struct clk_composite {
struct clk_hw hw;
struct clk_ops ops;
struct clk_hw *mux_hw;
struct clk_hw *rate_hw;
struct clk_hw *gate_hw;
const struct clk_ops *mux_ops;
const struct clk_ops *rate_ops;
const struct clk_ops *gate_ops;
};
struct clk *clk_register_composite(struct device *dev, const char *name,
const char **parent_names, int num_parents,
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags);
/** /**
* clk_register - allocate a new clock, register it and return an opaque cookie * clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock * @dev: device that is registering this clock
@ -351,6 +407,7 @@ unsigned int __clk_get_enable_count(struct clk *clk);
unsigned int __clk_get_prepare_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk);
unsigned long __clk_get_rate(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk);
unsigned long __clk_get_flags(struct clk *clk); unsigned long __clk_get_flags(struct clk *clk);
bool __clk_is_prepared(struct clk *clk);
bool __clk_is_enabled(struct clk *clk); bool __clk_is_enabled(struct clk *clk);
struct clk *__clk_lookup(const char *name); struct clk *__clk_lookup(const char *name);

View File

@ -28,16 +28,16 @@ struct clk;
* PRE_RATE_CHANGE - called immediately before the clk rate is changed, * PRE_RATE_CHANGE - called immediately before the clk rate is changed,
* to indicate that the rate change will proceed. Drivers must * to indicate that the rate change will proceed. Drivers must
* immediately terminate any operations that will be affected by the * immediately terminate any operations that will be affected by the
* rate change. Callbacks may either return NOTIFY_DONE or * rate change. Callbacks may either return NOTIFY_DONE, NOTIFY_OK,
* NOTIFY_STOP. * NOTIFY_STOP or NOTIFY_BAD.
* *
* ABORT_RATE_CHANGE: called if the rate change failed for some reason * ABORT_RATE_CHANGE: called if the rate change failed for some reason
* after PRE_RATE_CHANGE. In this case, all registered notifiers on * after PRE_RATE_CHANGE. In this case, all registered notifiers on
* the clk will be called with ABORT_RATE_CHANGE. Callbacks must * the clk will be called with ABORT_RATE_CHANGE. Callbacks must
* always return NOTIFY_DONE. * always return NOTIFY_DONE or NOTIFY_OK.
* *
* POST_RATE_CHANGE - called after the clk rate change has successfully * POST_RATE_CHANGE - called after the clk rate change has successfully
* completed. Callbacks must always return NOTIFY_DONE. * completed. Callbacks must always return NOTIFY_DONE or NOTIFY_OK.
* *
*/ */
#define PRE_RATE_CHANGE BIT(0) #define PRE_RATE_CHANGE BIT(0)

22
include/linux/clk/sunxi.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright 2012 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_CLK_SUNXI_H_
#define __LINUX_CLK_SUNXI_H_
void __init sunxi_init_clocks(void);
#endif

View File

@ -0,0 +1,114 @@
/*
* Si5351A/B/C programmable clock generator platform_data.
*/
#ifndef __LINUX_PLATFORM_DATA_SI5351_H__
#define __LINUX_PLATFORM_DATA_SI5351_H__
struct clk;
/**
* enum si5351_variant - SiLabs Si5351 chip variant
* @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
* @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
* @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
* @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
*/
enum si5351_variant {
SI5351_VARIANT_A = 1,
SI5351_VARIANT_A3 = 2,
SI5351_VARIANT_B = 3,
SI5351_VARIANT_C = 4,
};
/**
* enum si5351_pll_src - Si5351 pll clock source
* @SI5351_PLL_SRC_DEFAULT: default, do not change eeprom config
* @SI5351_PLL_SRC_XTAL: pll source clock is XTAL input
* @SI5351_PLL_SRC_CLKIN: pll source clock is CLKIN input (Si5351C only)
*/
enum si5351_pll_src {
SI5351_PLL_SRC_DEFAULT = 0,
SI5351_PLL_SRC_XTAL = 1,
SI5351_PLL_SRC_CLKIN = 2,
};
/**
* enum si5351_multisynth_src - Si5351 multisynth clock source
* @SI5351_MULTISYNTH_SRC_DEFAULT: default, do not change eeprom config
* @SI5351_MULTISYNTH_SRC_VCO0: multisynth source clock is VCO0
* @SI5351_MULTISYNTH_SRC_VCO1: multisynth source clock is VCO1/VXCO
*/
enum si5351_multisynth_src {
SI5351_MULTISYNTH_SRC_DEFAULT = 0,
SI5351_MULTISYNTH_SRC_VCO0 = 1,
SI5351_MULTISYNTH_SRC_VCO1 = 2,
};
/**
* enum si5351_clkout_src - Si5351 clock output clock source
* @SI5351_CLKOUT_SRC_DEFAULT: default, do not change eeprom config
* @SI5351_CLKOUT_SRC_MSYNTH_N: clkout N source clock is multisynth N
* @SI5351_CLKOUT_SRC_MSYNTH_0_4: clkout N source clock is multisynth 0 (N<4)
* or 4 (N>=4)
* @SI5351_CLKOUT_SRC_XTAL: clkout N source clock is XTAL
* @SI5351_CLKOUT_SRC_CLKIN: clkout N source clock is CLKIN (Si5351C only)
*/
enum si5351_clkout_src {
SI5351_CLKOUT_SRC_DEFAULT = 0,
SI5351_CLKOUT_SRC_MSYNTH_N = 1,
SI5351_CLKOUT_SRC_MSYNTH_0_4 = 2,
SI5351_CLKOUT_SRC_XTAL = 3,
SI5351_CLKOUT_SRC_CLKIN = 4,
};
/**
* enum si5351_drive_strength - Si5351 clock output drive strength
* @SI5351_DRIVE_DEFAULT: default, do not change eeprom config
* @SI5351_DRIVE_2MA: 2mA clock output drive strength
* @SI5351_DRIVE_4MA: 4mA clock output drive strength
* @SI5351_DRIVE_6MA: 6mA clock output drive strength
* @SI5351_DRIVE_8MA: 8mA clock output drive strength
*/
enum si5351_drive_strength {
SI5351_DRIVE_DEFAULT = 0,
SI5351_DRIVE_2MA = 2,
SI5351_DRIVE_4MA = 4,
SI5351_DRIVE_6MA = 6,
SI5351_DRIVE_8MA = 8,
};
/**
* struct si5351_clkout_config - Si5351 clock output configuration
* @clkout: clkout number
* @multisynth_src: multisynth source clock
* @clkout_src: clkout source clock
* @pll_master: if true, clkout can also change pll rate
* @drive: output drive strength
* @rate: initial clkout rate, or default if 0
*/
struct si5351_clkout_config {
enum si5351_multisynth_src multisynth_src;
enum si5351_clkout_src clkout_src;
enum si5351_drive_strength drive;
bool pll_master;
unsigned long rate;
};
/**
* struct si5351_platform_data - Platform data for the Si5351 clock driver
* @variant: Si5351 chip variant
* @clk_xtal: xtal input clock
* @clk_clkin: clkin input clock
* @pll_src: array of pll source clock setting
* @clkout: array of clkout configuration
*/
struct si5351_platform_data {
enum si5351_variant variant;
struct clk *clk_xtal;
struct clk *clk_clkin;
enum si5351_pll_src pll_src[2];
struct si5351_clkout_config clkout[8];
};
#endif