AT91: Move to Common Clock Framework and sama5d3 implementation
This is the first step to move AT91 to the CCF. - core CCF and drivers for most of the clocks - use of CCF for sama5d3 (100% DT-based) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQEcBAABAgAGBQJSnLhVAAoJEAf03oE53VmQugkH/11ZuSaLsjn31/WvE4WwKdgc RCIx7r8BCmPLNwDFOgc7tsheH7Jb6I7BNv4MnX8NIPrcAGy6yCEaD8TqhwKGTs9s 2YGoKB48fpwm1udDc6Hfq3/yqkPvM7AnzE1ei1rJNmLt2582tlX1FI+klQG5EoUK 23Pf4yjxtxt31s6xjuvRjEVjEat4PWnuVLs0nZWK6mizApXjA1JPIsKS6NL7q+eG aBMWDGw0DW4M8VEHAZygxF16PRXnumuB7NAIxKp8SJW5/acSeQntJTNXisMQBem7 DxWfvK7WAFEBGUcRN33a9RpKaHg0Gur1If2sw6CkZ83RnRV9CLqURty1mO4WCUo= =B+yP -----END PGP SIGNATURE----- Merge tag 'at91-cleanup' of git://github.com/at91linux/linux-at91 into next/cleanup From Nicolas Ferre: AT91: Move to Common Clock Framework and sama5d3 implementation This is the first step to move AT91 to the CCF. - core CCF and drivers for most of the clocks - use of CCF for sama5d3 (100% DT-based) * tag 'at91-cleanup' of git://github.com/at91linux/linux-at91: (22 commits) ARM: at91/dt: remove old clk material ARM: at91: move sama5d3 SoC to common clk ARM: at91/dt: define sama5d3xek's main clk frequency ARM: at91/dt: define sama5d3 clocks ARM: at91: prepare common clk transition for sama5d3 SoC ARM: at91: prepare sama5 dt boards transition to common clk ARM: at91: add new compatible strings for pmc driver ARM: at91: move pit timer to common clk framework dt: binding: add at91 clks dt bindings documentation clk: at91: add PMC smd clock clk: at91: add PMC usb clock clk: at91: add PMC utmi clock clk: at91: add PMC programmable clocks clk: at91: add PMC peripheral clocks clk: at91: add PMC system clocks clk: at91: add PMC master clock clk: at91: add PMC pll clocks clk: at91: add PMC main clock clk: at91: add PMC macro file for dt definitions clk: at91: add PMC base support ... Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
94c5216ee9
|
@ -0,0 +1,339 @@
|
|||
Device Tree Clock bindings for arch-at91
|
||||
|
||||
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:
|
||||
"atmel,at91rm9200-pmc" or
|
||||
"atmel,at91sam9g45-pmc" or
|
||||
"atmel,at91sam9n12-pmc" or
|
||||
"atmel,at91sam9x5-pmc" or
|
||||
"atmel,sama5d3-pmc":
|
||||
at91 PMC (Power Management Controller)
|
||||
All at91 specific clocks (clocks defined below) must be child
|
||||
node of the PMC node.
|
||||
|
||||
"atmel,at91rm9200-clk-main":
|
||||
at91 main oscillator
|
||||
|
||||
"atmel,at91rm9200-clk-master" or
|
||||
"atmel,at91sam9x5-clk-master":
|
||||
at91 master clock
|
||||
|
||||
"atmel,at91sam9x5-clk-peripheral" or
|
||||
"atmel,at91rm9200-clk-peripheral":
|
||||
at91 peripheral clocks
|
||||
|
||||
"atmel,at91rm9200-clk-pll" or
|
||||
"atmel,at91sam9g45-clk-pll" or
|
||||
"atmel,at91sam9g20-clk-pllb" or
|
||||
"atmel,sama5d3-clk-pll":
|
||||
at91 pll clocks
|
||||
|
||||
"atmel,at91sam9x5-clk-plldiv":
|
||||
at91 plla divisor
|
||||
|
||||
"atmel,at91rm9200-clk-programmable" or
|
||||
"atmel,at91sam9g45-clk-programmable" or
|
||||
"atmel,at91sam9x5-clk-programmable":
|
||||
at91 programmable clocks
|
||||
|
||||
"atmel,at91sam9x5-clk-smd":
|
||||
at91 SMD (Soft Modem) clock
|
||||
|
||||
"atmel,at91rm9200-clk-system":
|
||||
at91 system clocks
|
||||
|
||||
"atmel,at91rm9200-clk-usb" or
|
||||
"atmel,at91sam9x5-clk-usb" or
|
||||
"atmel,at91sam9n12-clk-usb":
|
||||
at91 usb clock
|
||||
|
||||
"atmel,at91sam9x5-clk-utmi":
|
||||
at91 utmi clock
|
||||
|
||||
Required properties for PMC node:
|
||||
- reg : defines the IO memory reserved for the PMC.
|
||||
- #size-cells : shall be 0 (reg is used to encode clk id).
|
||||
- #address-cells : shall be 1 (reg is used to encode clk id).
|
||||
- interrupts : shall be set to PMC interrupt line.
|
||||
- interrupt-controller : tell that the PMC is an interrupt controller.
|
||||
- #interrupt-cells : must be set to 1. The first cell encodes the interrupt id,
|
||||
and reflect the bit position in the PMC_ER/DR/SR registers.
|
||||
You can use the dt macros defined in dt-bindings/clk/at91.h.
|
||||
0 (AT91_PMC_MOSCS) -> main oscillator ready
|
||||
1 (AT91_PMC_LOCKA) -> PLL A ready
|
||||
2 (AT91_PMC_LOCKB) -> PLL B ready
|
||||
3 (AT91_PMC_MCKRDY) -> master clock ready
|
||||
6 (AT91_PMC_LOCKU) -> UTMI PLL clock ready
|
||||
8 .. 15 (AT91_PMC_PCKRDY(id)) -> programmable clock ready
|
||||
16 (AT91_PMC_MOSCSELS) -> main oscillator selected
|
||||
17 (AT91_PMC_MOSCRCS) -> RC main oscillator stabilized
|
||||
18 (AT91_PMC_CFDEV) -> clock failure detected
|
||||
|
||||
For example:
|
||||
pmc: pmc@fffffc00 {
|
||||
compatible = "atmel,sama5d3-pmc";
|
||||
interrupts = <1 4 7>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
|
||||
/* put at91 clocks here */
|
||||
};
|
||||
|
||||
Required properties for main clock:
|
||||
- interrupt-parent : must reference the PMC node.
|
||||
- interrupts : shall be set to "<0>".
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks (optional if clock-frequency is provided) : shall be the slow clock
|
||||
phandle. This clock is used to calculate the main clock rate if
|
||||
"clock-frequency" is not provided.
|
||||
- clock-frequency : the main oscillator frequency.Prefer the use of
|
||||
"clock-frequency" over automatic clock rate calculation.
|
||||
|
||||
For example:
|
||||
main: mainck {
|
||||
compatible = "atmel,at91rm9200-clk-main";
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <0>;
|
||||
#clock-cells = <0>;
|
||||
clocks = <&ck32k>;
|
||||
clock-frequency = <18432000>;
|
||||
};
|
||||
|
||||
Required properties for master clock:
|
||||
- interrupt-parent : must reference the PMC node.
|
||||
- interrupts : shall be set to "<3>".
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the master clock sources (see atmel datasheet) phandles.
|
||||
e.g. "<&ck32k>, <&main>, <&plla>, <&pllb>".
|
||||
- atmel,clk-output-range : minimum and maximum clock frequency (two u32
|
||||
fields).
|
||||
e.g. output = <0 133000000>; <=> 0 to 133MHz.
|
||||
- atmel,clk-divisors : master clock divisors table (four u32 fields).
|
||||
0 <=> reserved value.
|
||||
e.g. divisors = <1 2 4 6>;
|
||||
- atmel,master-clk-have-div3-pres : some SoC use the reserved value 7 in the
|
||||
PRES field as CLOCK_DIV3 (e.g sam9x5).
|
||||
|
||||
For example:
|
||||
mck: mck {
|
||||
compatible = "atmel,at91rm9200-clk-master";
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <3>;
|
||||
#clock-cells = <0>;
|
||||
atmel,clk-output-range = <0 133000000>;
|
||||
atmel,clk-divisors = <1 2 4 0>;
|
||||
};
|
||||
|
||||
Required properties for peripheral clocks:
|
||||
- #size-cells : shall be 0 (reg is used to encode clk id).
|
||||
- #address-cells : shall be 1 (reg is used to encode clk id).
|
||||
- clocks : shall be the master clock phandle.
|
||||
e.g. clocks = <&mck>;
|
||||
- name: device tree node describing a specific system clock.
|
||||
* #clock-cells : from common clock binding; shall be set to 0.
|
||||
* reg: peripheral id. See Atmel's datasheets to get a full
|
||||
list of peripheral ids.
|
||||
* atmel,clk-output-range : minimum and maximum clock frequency
|
||||
(two u32 fields). Only valid on at91sam9x5-clk-peripheral
|
||||
compatible IPs.
|
||||
|
||||
For example:
|
||||
periph: periphck {
|
||||
compatible = "atmel,at91sam9x5-clk-peripheral";
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
clocks = <&mck>;
|
||||
|
||||
ssc0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <2>;
|
||||
atmel,clk-output-range = <0 133000000>;
|
||||
};
|
||||
|
||||
usart0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <3>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Required properties for pll clocks:
|
||||
- interrupt-parent : must reference the PMC node.
|
||||
- interrupts : shall be set to "<1>".
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the main clock phandle.
|
||||
- reg : pll id.
|
||||
0 -> PLL A
|
||||
1 -> PLL B
|
||||
- atmel,clk-input-range : minimum and maximum source clock frequency (two u32
|
||||
fields).
|
||||
e.g. input = <1 32000000>; <=> 1 to 32MHz.
|
||||
- #atmel,pll-clk-output-range-cells : number of cells reserved for pll output
|
||||
range description. Sould be set to 2, 3
|
||||
or 4.
|
||||
* 1st and 2nd cells represent the frequency range (min-max).
|
||||
* 3rd cell is optional and represents the OUT field value for the given
|
||||
range.
|
||||
* 4th cell is optional and represents the ICPLL field (PLLICPR
|
||||
register)
|
||||
- atmel,pll-clk-output-ranges : pll output frequency ranges + optional parameter
|
||||
depending on #atmel,pll-output-range-cells
|
||||
property value.
|
||||
|
||||
For example:
|
||||
plla: pllack {
|
||||
compatible = "atmel,at91sam9g45-clk-pll";
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <1>;
|
||||
#clock-cells = <0>;
|
||||
clocks = <&main>;
|
||||
reg = <0>;
|
||||
atmel,clk-input-range = <2000000 32000000>;
|
||||
#atmel,pll-clk-output-range-cells = <4>;
|
||||
atmel,pll-clk-output-ranges = <74500000 800000000 0 0
|
||||
69500000 750000000 1 0
|
||||
64500000 700000000 2 0
|
||||
59500000 650000000 3 0
|
||||
54500000 600000000 0 1
|
||||
49500000 550000000 1 1
|
||||
44500000 500000000 2 1
|
||||
40000000 450000000 3 1>;
|
||||
};
|
||||
|
||||
Required properties for plldiv clocks (plldiv = pll / 2):
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the plla clock phandle.
|
||||
|
||||
The pll divisor is equal to 2 and cannot be changed.
|
||||
|
||||
For example:
|
||||
plladiv: plladivck {
|
||||
compatible = "atmel,at91sam9x5-clk-plldiv";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plla>;
|
||||
};
|
||||
|
||||
Required properties for programmable clocks:
|
||||
- interrupt-parent : must reference the PMC node.
|
||||
- #size-cells : shall be 0 (reg is used to encode clk id).
|
||||
- #address-cells : shall be 1 (reg is used to encode clk id).
|
||||
- clocks : shall be the programmable clock source phandles.
|
||||
e.g. clocks = <&clk32k>, <&main>, <&plla>, <&pllb>;
|
||||
- name: device tree node describing a specific prog clock.
|
||||
* #clock-cells : from common clock binding; shall be set to 0.
|
||||
* reg : programmable clock id (register offset from PCKx
|
||||
register).
|
||||
* interrupts : shall be set to "<(8 + id)>".
|
||||
|
||||
For example:
|
||||
prog: progck {
|
||||
compatible = "atmel,at91sam9g45-clk-programmable";
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
interrupt-parent = <&pmc>;
|
||||
clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
|
||||
|
||||
prog0 {
|
||||
#clock-cells = <0>;
|
||||
reg = <0>;
|
||||
interrupts = <8>;
|
||||
};
|
||||
|
||||
prog1 {
|
||||
#clock-cells = <0>;
|
||||
reg = <1>;
|
||||
interrupts = <9>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Required properties for smd clock:
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the smd clock source phandles.
|
||||
e.g. clocks = <&plladiv>, <&utmi>;
|
||||
|
||||
For example:
|
||||
smd: smdck {
|
||||
compatible = "atmel,at91sam9x5-clk-smd";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plladiv>, <&utmi>;
|
||||
};
|
||||
|
||||
Required properties for system clocks:
|
||||
- #size-cells : shall be 0 (reg is used to encode clk id).
|
||||
- #address-cells : shall be 1 (reg is used to encode clk id).
|
||||
- name: device tree node describing a specific system clock.
|
||||
* #clock-cells : from common clock binding; shall be set to 0.
|
||||
* reg: system clock id (bit position in SCER/SCDR/SCSR registers).
|
||||
See Atmel's datasheet to get a full list of system clock ids.
|
||||
|
||||
For example:
|
||||
system: systemck {
|
||||
compatible = "atmel,at91rm9200-clk-system";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ddrck {
|
||||
#clock-cells = <0>;
|
||||
reg = <2>;
|
||||
clocks = <&mck>;
|
||||
};
|
||||
|
||||
uhpck {
|
||||
#clock-cells = <0>;
|
||||
reg = <6>;
|
||||
clocks = <&usb>;
|
||||
};
|
||||
|
||||
udpck {
|
||||
#clock-cells = <0>;
|
||||
reg = <7>;
|
||||
clocks = <&usb>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Required properties for usb clock:
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the smd clock source phandles.
|
||||
e.g. clocks = <&pllb>;
|
||||
- atmel,clk-divisors (only available for "atmel,at91rm9200-clk-usb"):
|
||||
usb clock divisor table.
|
||||
e.g. divisors = <1 2 4 0>;
|
||||
|
||||
For example:
|
||||
usb: usbck {
|
||||
compatible = "atmel,at91sam9x5-clk-usb";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plladiv>, <&utmi>;
|
||||
};
|
||||
|
||||
usb: usbck {
|
||||
compatible = "atmel,at91rm9200-clk-usb";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&pllb>;
|
||||
atmel,clk-divisors = <1 2 4 0>;
|
||||
};
|
||||
|
||||
|
||||
Required properties for utmi clock:
|
||||
- interrupt-parent : must reference the PMC node.
|
||||
- interrupts : shall be set to "<AT91_PMC_LOCKU IRQ_TYPE_LEVEL_HIGH>".
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
- clocks : shall be the main clock source phandle.
|
||||
|
||||
For example:
|
||||
utmi: utmick {
|
||||
compatible = "atmel,at91sam9x5-clk-utmi";
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <AT91_PMC_LOCKU IRQ_TYPE_LEVEL_HIGH>;
|
||||
#clock-cells = <0>;
|
||||
clocks = <&main>;
|
||||
};
|
|
@ -13,6 +13,7 @@
|
|||
#include <dt-bindings/pinctrl/at91.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/clk/at91.h>
|
||||
|
||||
/ {
|
||||
model = "Atmel SAMA5D3 family SoC";
|
||||
|
@ -56,6 +57,14 @@
|
|||
reg = <0x20000000 0x8000000>;
|
||||
};
|
||||
|
||||
clocks {
|
||||
adc_op_clk: adc_op_clk{
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
|
||||
ahb {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
|
@ -79,6 +88,8 @@
|
|||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mci0_clk>;
|
||||
clock-names = "mci_clk";
|
||||
};
|
||||
|
||||
spi0: spi@f0004000 {
|
||||
|
@ -92,6 +103,8 @@
|
|||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
clocks = <&spi0_clk>;
|
||||
clock-names = "spi_clk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -101,6 +114,8 @@
|
|||
interrupts = <38 IRQ_TYPE_LEVEL_HIGH 4>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
|
||||
clocks = <&ssc0_clk>;
|
||||
clock-names = "pclk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -108,6 +123,8 @@
|
|||
compatible = "atmel,at91sam9x5-tcb";
|
||||
reg = <0xf0010000 0x100>;
|
||||
interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&tcb0_clk>;
|
||||
clock-names = "t0_clk";
|
||||
};
|
||||
|
||||
i2c0: i2c@f0014000 {
|
||||
|
@ -121,6 +138,7 @@
|
|||
pinctrl-0 = <&pinctrl_i2c0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&twi0_clk>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -135,6 +153,7 @@
|
|||
pinctrl-0 = <&pinctrl_i2c1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&twi1_clk>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -144,6 +163,8 @@
|
|||
interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usart0>;
|
||||
clocks = <&usart0_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -153,6 +174,8 @@
|
|||
interrupts = <13 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usart1>;
|
||||
clocks = <&usart1_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -174,6 +197,8 @@
|
|||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mci1_clk>;
|
||||
clock-names = "mci_clk";
|
||||
};
|
||||
|
||||
spi1: spi@f8008000 {
|
||||
|
@ -187,6 +212,8 @@
|
|||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
clocks = <&spi1_clk>;
|
||||
clock-names = "spi_clk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -196,6 +223,8 @@
|
|||
interrupts = <39 IRQ_TYPE_LEVEL_HIGH 4>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
|
||||
clocks = <&ssc1_clk>;
|
||||
clock-names = "pclk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -219,6 +248,9 @@
|
|||
&pinctrl_adc0_ad10
|
||||
&pinctrl_adc0_ad11
|
||||
>;
|
||||
clocks = <&adc_clk>,
|
||||
<&adc_op_clk>;
|
||||
clock-names = "adc_clk", "adc_op_clk";
|
||||
atmel,adc-channel-base = <0x50>;
|
||||
atmel,adc-channels-used = <0xfff>;
|
||||
atmel,adc-drdy-mask = <0x1000000>;
|
||||
|
@ -274,6 +306,7 @@
|
|||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&twi2_clk>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -283,6 +316,8 @@
|
|||
interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usart2>;
|
||||
clocks = <&usart2_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -292,6 +327,8 @@
|
|||
interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usart3>;
|
||||
clocks = <&usart3_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -318,6 +355,8 @@
|
|||
reg = <0xffffe600 0x200>;
|
||||
interrupts = <30 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
#dma-cells = <2>;
|
||||
clocks = <&dma0_clk>;
|
||||
clock-names = "dma_clk";
|
||||
};
|
||||
|
||||
dma1: dma-controller@ffffe800 {
|
||||
|
@ -325,6 +364,8 @@
|
|||
reg = <0xffffe800 0x200>;
|
||||
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
#dma-cells = <2>;
|
||||
clocks = <&dma1_clk>;
|
||||
clock-names = "dma_clk";
|
||||
};
|
||||
|
||||
ramc0: ramc@ffffea00 {
|
||||
|
@ -338,6 +379,8 @@
|
|||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_dbgu>;
|
||||
clocks = <&dbgu_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -626,6 +669,7 @@
|
|||
gpio-controller;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&pioA_clk>;
|
||||
};
|
||||
|
||||
pioB: gpio@fffff400 {
|
||||
|
@ -636,6 +680,7 @@
|
|||
gpio-controller;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&pioB_clk>;
|
||||
};
|
||||
|
||||
pioC: gpio@fffff600 {
|
||||
|
@ -646,6 +691,7 @@
|
|||
gpio-controller;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&pioC_clk>;
|
||||
};
|
||||
|
||||
pioD: gpio@fffff800 {
|
||||
|
@ -656,6 +702,7 @@
|
|||
gpio-controller;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&pioD_clk>;
|
||||
};
|
||||
|
||||
pioE: gpio@fffffa00 {
|
||||
|
@ -666,12 +713,334 @@
|
|||
gpio-controller;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&pioE_clk>;
|
||||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
compatible = "atmel,at91rm9200-pmc";
|
||||
compatible = "atmel,sama5d3-pmc";
|
||||
reg = <0xfffffc00 0x120>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
interrupt-controller;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
|
||||
clk32k: slck {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
|
||||
main: mainck {
|
||||
compatible = "atmel,at91rm9200-clk-main";
|
||||
#clock-cells = <0>;
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <AT91_PMC_MOSCS>;
|
||||
clocks = <&clk32k>;
|
||||
};
|
||||
|
||||
plla: pllack {
|
||||
compatible = "atmel,sama5d3-clk-pll";
|
||||
#clock-cells = <0>;
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <AT91_PMC_LOCKA>;
|
||||
clocks = <&main>;
|
||||
reg = <0>;
|
||||
atmel,clk-input-range = <8000000 50000000>;
|
||||
#atmel,pll-clk-output-range-cells = <4>;
|
||||
atmel,pll-clk-output-ranges = <400000000 1000000000 0 0>;
|
||||
};
|
||||
|
||||
plladiv: plladivck {
|
||||
compatible = "atmel,at91sam9x5-clk-plldiv";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plla>;
|
||||
};
|
||||
|
||||
utmi: utmick {
|
||||
compatible = "atmel,at91sam9x5-clk-utmi";
|
||||
#clock-cells = <0>;
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <AT91_PMC_LOCKU>;
|
||||
clocks = <&main>;
|
||||
};
|
||||
|
||||
mck: masterck {
|
||||
compatible = "atmel,at91sam9x5-clk-master";
|
||||
#clock-cells = <0>;
|
||||
interrupt-parent = <&pmc>;
|
||||
interrupts = <AT91_PMC_MCKRDY>;
|
||||
clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>;
|
||||
atmel,clk-output-range = <0 166000000>;
|
||||
atmel,clk-divisors = <1 2 4 3>;
|
||||
};
|
||||
|
||||
usb: usbck {
|
||||
compatible = "atmel,at91sam9x5-clk-usb";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plladiv>, <&utmi>;
|
||||
};
|
||||
|
||||
prog: progck {
|
||||
compatible = "atmel,at91sam9x5-clk-programmable";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupt-parent = <&pmc>;
|
||||
clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
|
||||
|
||||
prog0: prog0 {
|
||||
#clock-cells = <0>;
|
||||
reg = <0>;
|
||||
interrupts = <AT91_PMC_PCKRDY(0)>;
|
||||
};
|
||||
|
||||
prog1: prog1 {
|
||||
#clock-cells = <0>;
|
||||
reg = <1>;
|
||||
interrupts = <AT91_PMC_PCKRDY(1)>;
|
||||
};
|
||||
|
||||
prog2: prog2 {
|
||||
#clock-cells = <0>;
|
||||
reg = <2>;
|
||||
interrupts = <AT91_PMC_PCKRDY(2)>;
|
||||
};
|
||||
};
|
||||
|
||||
smd: smdclk {
|
||||
compatible = "atmel,at91sam9x5-clk-smd";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&plladiv>, <&utmi>;
|
||||
};
|
||||
|
||||
systemck {
|
||||
compatible = "atmel,at91rm9200-clk-system";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ddrck: ddrck {
|
||||
#clock-cells = <0>;
|
||||
reg = <2>;
|
||||
clocks = <&mck>;
|
||||
};
|
||||
|
||||
smdck: smdck {
|
||||
#clock-cells = <0>;
|
||||
reg = <4>;
|
||||
clocks = <&smd>;
|
||||
};
|
||||
|
||||
uhpck: uhpck {
|
||||
#clock-cells = <0>;
|
||||
reg = <6>;
|
||||
clocks = <&usb>;
|
||||
};
|
||||
|
||||
udpck: udpck {
|
||||
#clock-cells = <0>;
|
||||
reg = <7>;
|
||||
clocks = <&usb>;
|
||||
};
|
||||
|
||||
pck0: pck0 {
|
||||
#clock-cells = <0>;
|
||||
reg = <8>;
|
||||
clocks = <&prog0>;
|
||||
};
|
||||
|
||||
pck1: pck1 {
|
||||
#clock-cells = <0>;
|
||||
reg = <9>;
|
||||
clocks = <&prog1>;
|
||||
};
|
||||
|
||||
pck2: pck2 {
|
||||
#clock-cells = <0>;
|
||||
reg = <10>;
|
||||
clocks = <&prog2>;
|
||||
};
|
||||
};
|
||||
|
||||
periphck {
|
||||
compatible = "atmel,at91sam9x5-clk-peripheral";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mck>;
|
||||
|
||||
dbgu_clk: dbgu_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
pioA_clk: pioA_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <6>;
|
||||
};
|
||||
|
||||
pioB_clk: pioB_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <7>;
|
||||
};
|
||||
|
||||
pioC_clk: pioC_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <8>;
|
||||
};
|
||||
|
||||
pioD_clk: pioD_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <9>;
|
||||
};
|
||||
|
||||
pioE_clk: pioE_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <10>;
|
||||
};
|
||||
|
||||
usart0_clk: usart0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <12>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
usart1_clk: usart1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <13>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
usart2_clk: usart2_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <14>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
usart3_clk: usart3_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <15>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
twi0_clk: twi0_clk {
|
||||
reg = <18>;
|
||||
#clock-cells = <0>;
|
||||
atmel,clk-output-range = <0 16625000>;
|
||||
};
|
||||
|
||||
twi1_clk: twi1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <19>;
|
||||
atmel,clk-output-range = <0 16625000>;
|
||||
};
|
||||
|
||||
twi2_clk: twi2_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <20>;
|
||||
atmel,clk-output-range = <0 16625000>;
|
||||
};
|
||||
|
||||
mci0_clk: mci0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <21>;
|
||||
};
|
||||
|
||||
mci1_clk: mci1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <22>;
|
||||
};
|
||||
|
||||
spi0_clk: spi0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <24>;
|
||||
atmel,clk-output-range = <0 133000000>;
|
||||
};
|
||||
|
||||
spi1_clk: spi1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <25>;
|
||||
atmel,clk-output-range = <0 133000000>;
|
||||
};
|
||||
|
||||
tcb0_clk: tcb0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <26>;
|
||||
atmel,clk-output-range = <0 133000000>;
|
||||
};
|
||||
|
||||
pwm_clk: pwm_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <28>;
|
||||
};
|
||||
|
||||
adc_clk: adc_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <29>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
dma0_clk: dma0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <30>;
|
||||
};
|
||||
|
||||
dma1_clk: dma1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <31>;
|
||||
};
|
||||
|
||||
uhphs_clk: uhphs_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <32>;
|
||||
};
|
||||
|
||||
udphs_clk: udphs_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <33>;
|
||||
};
|
||||
|
||||
isi_clk: isi_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <37>;
|
||||
};
|
||||
|
||||
ssc0_clk: ssc0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <38>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
ssc1_clk: ssc1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <39>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
sha_clk: sha_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <42>;
|
||||
};
|
||||
|
||||
aes_clk: aes_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <43>;
|
||||
};
|
||||
|
||||
tdes_clk: tdes_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <44>;
|
||||
};
|
||||
|
||||
trng_clk: trng_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <45>;
|
||||
};
|
||||
|
||||
fuse_clk: fuse_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <48>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
rstc@fffffe00 {
|
||||
|
@ -683,6 +1052,7 @@
|
|||
compatible = "atmel,at91sam9260-pit";
|
||||
reg = <0xfffffe30 0xf>;
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
clocks = <&mck>;
|
||||
};
|
||||
|
||||
watchdog@fffffe40 {
|
||||
|
@ -705,6 +1075,8 @@
|
|||
reg = <0x00500000 0x100000
|
||||
0xf8030000 0x4000>;
|
||||
interrupts = <33 IRQ_TYPE_LEVEL_HIGH 2>;
|
||||
clocks = <&udphs_clk>, <&utmi>;
|
||||
clock-names = "pclk", "hclk";
|
||||
status = "disabled";
|
||||
|
||||
ep0 {
|
||||
|
@ -817,6 +1189,9 @@
|
|||
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
|
||||
reg = <0x00600000 0x100000>;
|
||||
interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
|
||||
clocks = <&usb>, <&uhphs_clk>, <&udphs_clk>,
|
||||
<&uhpck>;
|
||||
clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -824,6 +1199,8 @@
|
|||
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
|
||||
reg = <0x00700000 0x100000>;
|
||||
interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
|
||||
clocks = <&usb>, <&uhphs_clk>, <&uhpck>;
|
||||
clock-names = "usb_clk", "ehci_clk", "uhpck";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -32,12 +32,30 @@
|
|||
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
can0_clk: can0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <40>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
can1_clk: can0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <41>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
can0: can@f000c000 {
|
||||
compatible = "atmel,at91sam9x5-can";
|
||||
reg = <0xf000c000 0x300>;
|
||||
interrupts = <40 IRQ_TYPE_LEVEL_HIGH 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_can0_rx_tx>;
|
||||
clocks = <&can0_clk>;
|
||||
clock-names = "can_clk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -47,6 +65,8 @@
|
|||
interrupts = <41 IRQ_TYPE_LEVEL_HIGH 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_can1_rx_tx>;
|
||||
clocks = <&can1_clk>;
|
||||
clock-names = "can_clk";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -31,12 +31,23 @@
|
|||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
macb1_clk: macb1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <35>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
macb1: ethernet@f802c000 {
|
||||
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
||||
reg = <0xf802c000 0x100>;
|
||||
interrupts = <35 IRQ_TYPE_LEVEL_HIGH 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_macb1_rmii>;
|
||||
clocks = <&macb1_clk>, <&macb1_clk>;
|
||||
clock-names = "hclk", "pclk";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -64,12 +64,23 @@
|
|||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
macb0_clk: macb0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <34>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
macb0: ethernet@f0028000 {
|
||||
compatible = "cdns,pc302-gem", "cdns,gem";
|
||||
reg = <0xf0028000 0x100>;
|
||||
interrupts = <34 IRQ_TYPE_LEVEL_HIGH 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_macb0_data_rgmii &pinctrl_macb0_signal_rgmii>;
|
||||
clocks = <&macb0_clk>, <&macb0_clk>;
|
||||
clock-names = "hclk", "pclk";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -50,6 +50,23 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
lcdc_clk: lcdc_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <36>;
|
||||
};
|
||||
};
|
||||
|
||||
systemck {
|
||||
lcdck: lcdck {
|
||||
#clock-cells = <0>;
|
||||
reg = <3>;
|
||||
clocks = <&mck>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <dt-bindings/pinctrl/at91.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clk/at91.h>
|
||||
|
||||
/ {
|
||||
ahb {
|
||||
|
@ -30,6 +31,15 @@
|
|||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
mci2_clk: mci2_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <23>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mmc2: mmc@f8004000 {
|
||||
compatible = "atmel,hsmci";
|
||||
reg = <0xf8004000 0x600>;
|
||||
|
@ -38,6 +48,8 @@
|
|||
dma-names = "rxtx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_mmc2_clk_cmd_dat0 &pinctrl_mmc2_dat1_3>;
|
||||
clocks = <&mci2_clk>;
|
||||
clock-names = "mci_clk";
|
||||
status = "disabled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <dt-bindings/pinctrl/at91.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clk/at91.h>
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
|
@ -17,10 +18,21 @@
|
|||
|
||||
ahb {
|
||||
apb {
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
tcb1_clk: tcb1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <27>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tcb1: timer@f8014000 {
|
||||
compatible = "atmel,at91sam9x5-tcb";
|
||||
reg = <0xf8014000 0x100>;
|
||||
interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&tcb1_clk>;
|
||||
clock-names = "t0_clk";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <dt-bindings/pinctrl/at91.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clk/at91.h>
|
||||
|
||||
/ {
|
||||
ahb {
|
||||
|
@ -31,12 +32,30 @@
|
|||
};
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
periphck {
|
||||
uart0_clk: uart0_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <16>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
|
||||
uart1_clk: uart1_clk {
|
||||
#clock-cells = <0>;
|
||||
reg = <17>;
|
||||
atmel,clk-output-range = <0 66000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@f0024000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xf0024000 0x200>;
|
||||
interrupts = <16 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_uart0>;
|
||||
clocks = <&uart0_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -46,6 +65,8 @@
|
|||
interrupts = <17 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_uart1>;
|
||||
clocks = <&uart1_clk>;
|
||||
clock-names = "usart";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,17 +18,6 @@
|
|||
reg = <0x20000000 0x20000000>;
|
||||
};
|
||||
|
||||
clocks {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
main_clock: clock@0 {
|
||||
compatible = "atmel,osc", "fixed-clock";
|
||||
clock-frequency = <12000000>;
|
||||
};
|
||||
};
|
||||
|
||||
ahb {
|
||||
apb {
|
||||
spi0: spi@f0004000 {
|
||||
|
@ -38,6 +27,12 @@
|
|||
macb0: ethernet@f0028000 {
|
||||
phy-mode = "rgmii";
|
||||
};
|
||||
|
||||
pmc: pmc@fffffc00 {
|
||||
main: mainck {
|
||||
clock-frequency = <12000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@60000000 {
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
if ARCH_AT91
|
||||
|
||||
config HAVE_AT91_UTMI
|
||||
bool
|
||||
|
||||
config HAVE_AT91_USB_CLK
|
||||
bool
|
||||
|
||||
config HAVE_AT91_DBGU0
|
||||
bool
|
||||
|
||||
config HAVE_AT91_DBGU1
|
||||
bool
|
||||
|
||||
config AT91_USE_OLD_CLK
|
||||
bool
|
||||
|
||||
config AT91_PMC_UNIT
|
||||
bool
|
||||
default !ARCH_AT91X40
|
||||
|
||||
config COMMON_CLK_AT91
|
||||
bool
|
||||
default AT91_PMC_UNIT && USE_OF && !AT91_USE_OLD_CLK
|
||||
select COMMON_CLK
|
||||
|
||||
config OLD_CLK_AT91
|
||||
bool
|
||||
default AT91_PMC_UNIT && AT91_USE_OLD_CLK
|
||||
|
||||
config AT91_SAM9_ALT_RESET
|
||||
bool
|
||||
default !ARCH_AT91X40
|
||||
|
@ -21,6 +39,9 @@ config AT91_SAM9G45_RESET
|
|||
config AT91_SAM9_TIME
|
||||
bool
|
||||
|
||||
config HAVE_AT91_SMD
|
||||
bool
|
||||
|
||||
config SOC_AT91SAM9
|
||||
bool
|
||||
select AT91_SAM9_TIME
|
||||
|
@ -65,6 +86,9 @@ config SOC_SAMA5D3
|
|||
select SOC_SAMA5
|
||||
select HAVE_FB_ATMEL
|
||||
select HAVE_AT91_DBGU1
|
||||
select HAVE_AT91_UTMI
|
||||
select HAVE_AT91_SMD
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using one of Atmel's SAMA5D3 family SoC.
|
||||
This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
|
||||
|
@ -78,11 +102,15 @@ config SOC_AT91RM9200
|
|||
select HAVE_AT91_DBGU0
|
||||
select MULTI_IRQ_HANDLER
|
||||
select SPARSE_IRQ
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_USB_CLK
|
||||
|
||||
config SOC_AT91SAM9260
|
||||
bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20"
|
||||
select HAVE_AT91_DBGU0
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE
|
||||
or AT91SAM9G20 SoC.
|
||||
|
@ -92,6 +120,8 @@ config SOC_AT91SAM9261
|
|||
select HAVE_AT91_DBGU0
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC.
|
||||
|
||||
|
@ -100,18 +130,25 @@ config SOC_AT91SAM9263
|
|||
select HAVE_AT91_DBGU1
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_USB_CLK
|
||||
|
||||
config SOC_AT91SAM9RL
|
||||
bool "AT91SAM9RL"
|
||||
select HAVE_AT91_DBGU0
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_UTMI
|
||||
|
||||
config SOC_AT91SAM9G45
|
||||
bool "AT91SAM9G45 or AT91SAM9M10 families"
|
||||
select HAVE_AT91_DBGU1
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_UTMI
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
|
||||
This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
|
||||
|
@ -121,6 +158,10 @@ config SOC_AT91SAM9X5
|
|||
select HAVE_AT91_DBGU0
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_UTMI
|
||||
select HAVE_AT91_SMD
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
|
||||
This means that your SAM9 name finishes with a '5' (except if it is
|
||||
|
@ -133,6 +174,8 @@ config SOC_AT91SAM9N12
|
|||
select HAVE_AT91_DBGU0
|
||||
select HAVE_FB_ATMEL
|
||||
select SOC_AT91SAM9
|
||||
select AT91_USE_OLD_CLK
|
||||
select HAVE_AT91_USB_CLK
|
||||
help
|
||||
Select this if you are using Atmel's AT91SAM9N12 SoC.
|
||||
|
||||
|
|
|
@ -12,26 +12,32 @@ config ARCH_AT91_NONE
|
|||
config ARCH_AT91RM9200
|
||||
bool "AT91RM9200"
|
||||
select SOC_AT91RM9200
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91SAM9260
|
||||
bool "AT91SAM9260 or AT91SAM9XE or AT91SAM9G20"
|
||||
select SOC_AT91SAM9260
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91SAM9261
|
||||
bool "AT91SAM9261 or AT91SAM9G10"
|
||||
select SOC_AT91SAM9261
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91SAM9263
|
||||
bool "AT91SAM9263"
|
||||
select SOC_AT91SAM9263
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91SAM9RL
|
||||
bool "AT91SAM9RL"
|
||||
select SOC_AT91SAM9RL
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91SAM9G45
|
||||
bool "AT91SAM9G45"
|
||||
select SOC_AT91SAM9G45
|
||||
select AT91_USE_OLD_CLK
|
||||
|
||||
config ARCH_AT91X40
|
||||
bool "AT91x40"
|
||||
|
|
|
@ -7,7 +7,7 @@ obj-m :=
|
|||
obj-n :=
|
||||
obj- :=
|
||||
|
||||
obj-$(CONFIG_AT91_PMC_UNIT) += clock.o
|
||||
obj-$(CONFIG_OLD_CLK_AT91) += clock.o
|
||||
obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o
|
||||
obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o
|
||||
obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <mach/at91rm9200.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/at91_st.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/irq.h>
|
||||
|
@ -20,7 +21,6 @@
|
|||
#include <mach/cpu.h>
|
||||
#include <mach/at91_dbgu.h>
|
||||
#include <mach/at91sam9260.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
#include "at91_rstc.h"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/irq.h>
|
||||
|
@ -19,7 +20,6 @@
|
|||
#include <asm/system_misc.h>
|
||||
#include <mach/cpu.h>
|
||||
#include <mach/at91sam9261.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
#include "at91_rstc.h"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/irq.h>
|
||||
|
@ -18,7 +19,6 @@
|
|||
#include <asm/mach/map.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <mach/at91sam9263.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
#include "at91_rstc.h"
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
static u32 pit_cycle; /* write-once */
|
||||
static u32 pit_cnt; /* access only w/system irq blocked */
|
||||
static void __iomem *pit_base_addr __read_mostly;
|
||||
static struct clk *mck;
|
||||
|
||||
static inline unsigned int pit_read(unsigned int reg_offset)
|
||||
{
|
||||
|
@ -195,10 +196,14 @@ static int __init of_at91sam926x_pit_init(void)
|
|||
if (!pit_base_addr)
|
||||
goto node_err;
|
||||
|
||||
mck = of_clk_get(np, 0);
|
||||
|
||||
/* Get the interrupts property */
|
||||
ret = irq_of_parse_and_map(np, 0);
|
||||
if (!ret) {
|
||||
pr_crit("AT91: PIT: Unable to get IRQ from DT\n");
|
||||
if (!IS_ERR(mck))
|
||||
clk_put(mck);
|
||||
goto ioremap_err;
|
||||
}
|
||||
at91sam926x_pit_irq.irq = ret;
|
||||
|
@ -230,6 +235,8 @@ void __init at91sam926x_pit_init(void)
|
|||
unsigned bits;
|
||||
int ret;
|
||||
|
||||
mck = ERR_PTR(-ENOENT);
|
||||
|
||||
/* For device tree enabled device: initialize here */
|
||||
of_at91sam926x_pit_init();
|
||||
|
||||
|
@ -237,7 +244,12 @@ void __init at91sam926x_pit_init(void)
|
|||
* Use our actual MCK to figure out how many MCK/16 ticks per
|
||||
* 1/HZ period (instead of a compile-time constant LATCH).
|
||||
*/
|
||||
pit_rate = clk_get_rate(clk_get(NULL, "mck")) / 16;
|
||||
if (IS_ERR(mck))
|
||||
mck = clk_get(NULL, "mck");
|
||||
|
||||
if (IS_ERR(mck))
|
||||
panic("AT91: PIT: Unable to get mck clk\n");
|
||||
pit_rate = clk_get_rate(mck) / 16;
|
||||
pit_cycle = (pit_rate + HZ/2) / HZ;
|
||||
WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <mach/at91sam9g45.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <mach/at91sam9n12.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "board.h"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/irq.h>
|
||||
|
@ -19,7 +20,6 @@
|
|||
#include <mach/cpu.h>
|
||||
#include <mach/at91_dbgu.h>
|
||||
#include <mach/at91sam9rl.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
#include "at91_rstc.h"
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <mach/at91sam9x5.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "board.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/irq.h>
|
||||
|
@ -26,6 +27,13 @@
|
|||
#include "at91_aic.h"
|
||||
#include "generic.h"
|
||||
|
||||
static void __init sama5_dt_timer_init(void)
|
||||
{
|
||||
#if defined(CONFIG_COMMON_CLK)
|
||||
of_clk_init(NULL);
|
||||
#endif
|
||||
at91sam926x_pit_init();
|
||||
}
|
||||
|
||||
static const struct of_device_id irq_of_match[] __initconst = {
|
||||
|
||||
|
@ -72,7 +80,7 @@ static const char *sama5_dt_board_compat[] __initdata = {
|
|||
|
||||
DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
|
||||
/* Maintainer: Atmel */
|
||||
.init_time = at91sam926x_pit_init,
|
||||
.init_time = sama5_dt_timer_init,
|
||||
.map_io = at91_map_io,
|
||||
.handle_irq = at91_aic5_handle_irq,
|
||||
.init_early = at91_dt_initialize,
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
|
@ -884,6 +884,11 @@ static int __init at91_pmc_init(unsigned long main_clock)
|
|||
#if defined(CONFIG_OF)
|
||||
static struct of_device_id pmc_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-pmc" },
|
||||
{ .compatible = "atmel,at91sam9260-pmc" },
|
||||
{ .compatible = "atmel,at91sam9g45-pmc" },
|
||||
{ .compatible = "atmel,at91sam9n12-pmc" },
|
||||
{ .compatible = "atmel,at91sam9x5-pmc" },
|
||||
{ .compatible = "atmel,sama5d3-pmc" },
|
||||
{ /*sentinel*/ }
|
||||
};
|
||||
|
||||
|
|
|
@ -46,11 +46,12 @@ extern void at91sam926x_pit_init(void);
|
|||
extern void at91x40_timer_init(void);
|
||||
|
||||
/* Clocks */
|
||||
#ifdef CONFIG_AT91_PMC_UNIT
|
||||
#ifdef CONFIG_OLD_CLK_AT91
|
||||
extern int __init at91_clock_init(unsigned long main_clock);
|
||||
extern int __init at91_dt_clock_init(void);
|
||||
#else
|
||||
static int inline at91_clock_init(unsigned long main_clock) { return 0; }
|
||||
static int inline at91_dt_clock_init(void) { return 0; }
|
||||
#endif
|
||||
struct device;
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/mach/time.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "at91_aic.h"
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/at91_ramc.h>
|
||||
|
||||
|
||||
|
|
|
@ -9,359 +9,18 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <mach/sama5d3.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "soc.h"
|
||||
#include "generic.h"
|
||||
#include "clock.h"
|
||||
#include "sam9_smc.h"
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Clocks
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* The peripheral clocks.
|
||||
*/
|
||||
|
||||
static struct clk pioA_clk = {
|
||||
.name = "pioA_clk",
|
||||
.pid = SAMA5D3_ID_PIOA,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk pioB_clk = {
|
||||
.name = "pioB_clk",
|
||||
.pid = SAMA5D3_ID_PIOB,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk pioC_clk = {
|
||||
.name = "pioC_clk",
|
||||
.pid = SAMA5D3_ID_PIOC,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk pioD_clk = {
|
||||
.name = "pioD_clk",
|
||||
.pid = SAMA5D3_ID_PIOD,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk pioE_clk = {
|
||||
.name = "pioE_clk",
|
||||
.pid = SAMA5D3_ID_PIOE,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk usart0_clk = {
|
||||
.name = "usart0_clk",
|
||||
.pid = SAMA5D3_ID_USART0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk usart1_clk = {
|
||||
.name = "usart1_clk",
|
||||
.pid = SAMA5D3_ID_USART1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk usart2_clk = {
|
||||
.name = "usart2_clk",
|
||||
.pid = SAMA5D3_ID_USART2,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk usart3_clk = {
|
||||
.name = "usart3_clk",
|
||||
.pid = SAMA5D3_ID_USART3,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk uart0_clk = {
|
||||
.name = "uart0_clk",
|
||||
.pid = SAMA5D3_ID_UART0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk uart1_clk = {
|
||||
.name = "uart1_clk",
|
||||
.pid = SAMA5D3_ID_UART1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk twi0_clk = {
|
||||
.name = "twi0_clk",
|
||||
.pid = SAMA5D3_ID_TWI0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV8,
|
||||
};
|
||||
static struct clk twi1_clk = {
|
||||
.name = "twi1_clk",
|
||||
.pid = SAMA5D3_ID_TWI1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV8,
|
||||
};
|
||||
static struct clk twi2_clk = {
|
||||
.name = "twi2_clk",
|
||||
.pid = SAMA5D3_ID_TWI2,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV8,
|
||||
};
|
||||
static struct clk mmc0_clk = {
|
||||
.name = "mci0_clk",
|
||||
.pid = SAMA5D3_ID_HSMCI0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk mmc1_clk = {
|
||||
.name = "mci1_clk",
|
||||
.pid = SAMA5D3_ID_HSMCI1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk mmc2_clk = {
|
||||
.name = "mci2_clk",
|
||||
.pid = SAMA5D3_ID_HSMCI2,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk spi0_clk = {
|
||||
.name = "spi0_clk",
|
||||
.pid = SAMA5D3_ID_SPI0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk spi1_clk = {
|
||||
.name = "spi1_clk",
|
||||
.pid = SAMA5D3_ID_SPI1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk tcb0_clk = {
|
||||
.name = "tcb0_clk",
|
||||
.pid = SAMA5D3_ID_TC0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk tcb1_clk = {
|
||||
.name = "tcb1_clk",
|
||||
.pid = SAMA5D3_ID_TC1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk adc_clk = {
|
||||
.name = "adc_clk",
|
||||
.pid = SAMA5D3_ID_ADC,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk adc_op_clk = {
|
||||
.name = "adc_op_clk",
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.rate_hz = 5000000,
|
||||
};
|
||||
static struct clk dma0_clk = {
|
||||
.name = "dma0_clk",
|
||||
.pid = SAMA5D3_ID_DMA0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk dma1_clk = {
|
||||
.name = "dma1_clk",
|
||||
.pid = SAMA5D3_ID_DMA1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk uhphs_clk = {
|
||||
.name = "uhphs",
|
||||
.pid = SAMA5D3_ID_UHPHS,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk udphs_clk = {
|
||||
.name = "udphs_clk",
|
||||
.pid = SAMA5D3_ID_UDPHS,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
/* gmac only for sama5d33, sama5d34, sama5d35 */
|
||||
static struct clk macb0_clk = {
|
||||
.name = "macb0_clk",
|
||||
.pid = SAMA5D3_ID_GMAC,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
/* emac only for sama5d31, sama5d35 */
|
||||
static struct clk macb1_clk = {
|
||||
.name = "macb1_clk",
|
||||
.pid = SAMA5D3_ID_EMAC,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
/* lcd only for sama5d31, sama5d33, sama5d34 */
|
||||
static struct clk lcdc_clk = {
|
||||
.name = "lcdc_clk",
|
||||
.pid = SAMA5D3_ID_LCDC,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
/* isi only for sama5d33, sama5d35 */
|
||||
static struct clk isi_clk = {
|
||||
.name = "isi_clk",
|
||||
.pid = SAMA5D3_ID_ISI,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk can0_clk = {
|
||||
.name = "can0_clk",
|
||||
.pid = SAMA5D3_ID_CAN0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk can1_clk = {
|
||||
.name = "can1_clk",
|
||||
.pid = SAMA5D3_ID_CAN1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk ssc0_clk = {
|
||||
.name = "ssc0_clk",
|
||||
.pid = SAMA5D3_ID_SSC0,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk ssc1_clk = {
|
||||
.name = "ssc1_clk",
|
||||
.pid = SAMA5D3_ID_SSC1,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV2,
|
||||
};
|
||||
static struct clk sha_clk = {
|
||||
.name = "sha_clk",
|
||||
.pid = SAMA5D3_ID_SHA,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
.div = AT91_PMC_PCR_DIV8,
|
||||
};
|
||||
static struct clk aes_clk = {
|
||||
.name = "aes_clk",
|
||||
.pid = SAMA5D3_ID_AES,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
static struct clk tdes_clk = {
|
||||
.name = "tdes_clk",
|
||||
.pid = SAMA5D3_ID_TDES,
|
||||
.type = CLK_TYPE_PERIPHERAL,
|
||||
};
|
||||
|
||||
static struct clk *periph_clocks[] __initdata = {
|
||||
&pioA_clk,
|
||||
&pioB_clk,
|
||||
&pioC_clk,
|
||||
&pioD_clk,
|
||||
&pioE_clk,
|
||||
&usart0_clk,
|
||||
&usart1_clk,
|
||||
&usart2_clk,
|
||||
&usart3_clk,
|
||||
&uart0_clk,
|
||||
&uart1_clk,
|
||||
&twi0_clk,
|
||||
&twi1_clk,
|
||||
&twi2_clk,
|
||||
&mmc0_clk,
|
||||
&mmc1_clk,
|
||||
&mmc2_clk,
|
||||
&spi0_clk,
|
||||
&spi1_clk,
|
||||
&tcb0_clk,
|
||||
&tcb1_clk,
|
||||
&adc_clk,
|
||||
&adc_op_clk,
|
||||
&dma0_clk,
|
||||
&dma1_clk,
|
||||
&uhphs_clk,
|
||||
&udphs_clk,
|
||||
&macb0_clk,
|
||||
&macb1_clk,
|
||||
&lcdc_clk,
|
||||
&isi_clk,
|
||||
&can0_clk,
|
||||
&can1_clk,
|
||||
&ssc0_clk,
|
||||
&ssc1_clk,
|
||||
&sha_clk,
|
||||
&aes_clk,
|
||||
&tdes_clk,
|
||||
};
|
||||
|
||||
static struct clk pck0 = {
|
||||
.name = "pck0",
|
||||
.pmc_mask = AT91_PMC_PCK0,
|
||||
.type = CLK_TYPE_PROGRAMMABLE,
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static struct clk pck1 = {
|
||||
.name = "pck1",
|
||||
.pmc_mask = AT91_PMC_PCK1,
|
||||
.type = CLK_TYPE_PROGRAMMABLE,
|
||||
.id = 1,
|
||||
};
|
||||
|
||||
static struct clk pck2 = {
|
||||
.name = "pck2",
|
||||
.pmc_mask = AT91_PMC_PCK2,
|
||||
.type = CLK_TYPE_PROGRAMMABLE,
|
||||
.id = 2,
|
||||
};
|
||||
|
||||
static struct clk_lookup periph_clocks_lookups[] = {
|
||||
/* lookup table for DT entries */
|
||||
CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff200.gpio", &pioA_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioB_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioC_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioD_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioE_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "f001c000.serial", &usart0_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "f0020000.serial", &usart1_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart2_clk),
|
||||
CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart3_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f0014000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f0018000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f801c000.i2c", &twi2_clk),
|
||||
CLKDEV_CON_DEV_ID("mci_clk", "f0000000.mmc", &mmc0_clk),
|
||||
CLKDEV_CON_DEV_ID("mci_clk", "f8000000.mmc", &mmc1_clk),
|
||||
CLKDEV_CON_DEV_ID("mci_clk", "f8004000.mmc", &mmc2_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f8008000.spi", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "f0010000.timer", &tcb0_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "f8014000.timer", &tcb1_clk),
|
||||
CLKDEV_CON_DEV_ID("tsc_clk", "f8018000.tsadcc", &adc_clk),
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffe600.dma-controller", &dma0_clk),
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffe800.dma-controller", &dma1_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "600000.ohci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID("ohci_clk", "600000.ohci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID("ehci_clk", "700000.ehci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "500000.gadget", &udphs_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "500000.gadget", &utmi_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "f0028000.ethernet", &macb0_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "f0028000.ethernet", &macb0_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "f802c000.ethernet", &macb1_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "f802c000.ethernet", &macb1_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "f0008000.ssc", &ssc0_clk),
|
||||
CLKDEV_CON_DEV_ID("pclk", "f000c000.ssc", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID("can_clk", "f000c000.can", &can0_clk),
|
||||
CLKDEV_CON_DEV_ID("can_clk", "f8010000.can", &can1_clk),
|
||||
CLKDEV_CON_DEV_ID("sha_clk", "f8034000.sha", &sha_clk),
|
||||
CLKDEV_CON_DEV_ID("aes_clk", "f8038000.aes", &aes_clk),
|
||||
CLKDEV_CON_DEV_ID("tdes_clk", "f803c000.tdes", &tdes_clk),
|
||||
};
|
||||
|
||||
static void __init sama5d3_register_clocks(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(periph_clocks); i++)
|
||||
clk_register(periph_clocks[i]);
|
||||
|
||||
clkdev_add_table(periph_clocks_lookups,
|
||||
ARRAY_SIZE(periph_clocks_lookups));
|
||||
|
||||
clk_register(&pck0);
|
||||
clk_register(&pck1);
|
||||
clk_register(&pck2);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* AT91SAM9x5 processor initialization
|
||||
* -------------------------------------------------------------------- */
|
||||
|
@ -378,6 +37,5 @@ static void __init sama5d3_initialize(void)
|
|||
|
||||
AT91_SOC_START(sama5d3)
|
||||
.map_io = sama5d3_map_io,
|
||||
.register_clocks = sama5d3_register_clocks,
|
||||
.init = sama5d3_initialize,
|
||||
AT91_SOC_END
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
@ -18,7 +19,6 @@
|
|||
#include <mach/hardware.h>
|
||||
#include <mach/cpu.h>
|
||||
#include <mach/at91_dbgu.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
#include "at91_shdwc.h"
|
||||
#include "soc.h"
|
||||
|
@ -491,7 +491,8 @@ void __init at91rm9200_dt_initialize(void)
|
|||
at91_dt_clock_init();
|
||||
|
||||
/* Register the processor-specific clocks */
|
||||
at91_boot_soc.register_clocks();
|
||||
if (at91_boot_soc.register_clocks)
|
||||
at91_boot_soc.register_clocks();
|
||||
|
||||
at91_boot_soc.init();
|
||||
}
|
||||
|
@ -506,7 +507,8 @@ void __init at91_dt_initialize(void)
|
|||
at91_dt_clock_init();
|
||||
|
||||
/* Register the processor-specific clocks */
|
||||
at91_boot_soc.register_clocks();
|
||||
if (at91_boot_soc.register_clocks)
|
||||
at91_boot_soc.register_clocks();
|
||||
|
||||
if (at91_boot_soc.init)
|
||||
at91_boot_soc.init();
|
||||
|
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
|||
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
|
||||
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
|
||||
obj-$(CONFIG_X86) += x86/
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for at91 specific clk
|
||||
#
|
||||
|
||||
obj-y += pmc.o
|
||||
obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
|
||||
obj-y += clk-system.o clk-peripheral.o
|
||||
|
||||
obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o
|
||||
obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
|
||||
obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
|
||||
obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define SLOW_CLOCK_FREQ 32768
|
||||
#define MAINF_DIV 16
|
||||
#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \
|
||||
SLOW_CLOCK_FREQ)
|
||||
#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ)
|
||||
#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT
|
||||
|
||||
struct clk_main {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned long rate;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
};
|
||||
|
||||
#define to_clk_main(hw) container_of(hw, struct clk_main, hw)
|
||||
|
||||
static irqreturn_t clk_main_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_main *clkmain = (struct clk_main *)dev_id;
|
||||
|
||||
wake_up(&clkmain->wait);
|
||||
disable_irq_nosync(clkmain->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_main_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_main *clkmain = to_clk_main(hw);
|
||||
struct at91_pmc *pmc = clkmain->pmc;
|
||||
unsigned long halt_time, timeout;
|
||||
u32 tmp;
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS)) {
|
||||
enable_irq(clkmain->irq);
|
||||
wait_event(clkmain->wait,
|
||||
pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
|
||||
}
|
||||
|
||||
if (clkmain->rate)
|
||||
return 0;
|
||||
|
||||
timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT);
|
||||
do {
|
||||
halt_time = jiffies;
|
||||
tmp = pmc_read(pmc, AT91_CKGR_MCFR);
|
||||
if (tmp & AT91_PMC_MAINRDY)
|
||||
return 0;
|
||||
usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
|
||||
} while (time_before(halt_time, timeout));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_main_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_main *clkmain = to_clk_main(hw);
|
||||
|
||||
return !!(pmc_read(clkmain->pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
|
||||
}
|
||||
|
||||
static unsigned long clk_main_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
struct clk_main *clkmain = to_clk_main(hw);
|
||||
struct at91_pmc *pmc = clkmain->pmc;
|
||||
|
||||
if (clkmain->rate)
|
||||
return clkmain->rate;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_CKGR_MCFR) & AT91_PMC_MAINF;
|
||||
clkmain->rate = (tmp * parent_rate) / MAINF_DIV;
|
||||
|
||||
return clkmain->rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops main_ops = {
|
||||
.prepare = clk_main_prepare,
|
||||
.is_prepared = clk_main_is_prepared,
|
||||
.recalc_rate = clk_main_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_main(struct at91_pmc *pmc,
|
||||
unsigned int irq,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
struct clk_main *clkmain;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
if (!pmc || !irq || !name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!rate && !parent_name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
|
||||
if (!clkmain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &main_ops;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
init.flags = parent_name ? 0 : CLK_IS_ROOT;
|
||||
|
||||
clkmain->hw.init = &init;
|
||||
clkmain->rate = rate;
|
||||
clkmain->pmc = pmc;
|
||||
clkmain->irq = irq;
|
||||
init_waitqueue_head(&clkmain->wait);
|
||||
irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN);
|
||||
ret = request_irq(clkmain->irq, clk_main_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, "clk-main", clkmain);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &clkmain->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
free_irq(clkmain->irq, clkmain);
|
||||
kfree(clkmain);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __init
|
||||
of_at91_clk_main_setup(struct device_node *np, struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
unsigned int irq;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
u32 rate = 0;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
of_property_read_u32(np, "clock-frequency", &rate);
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
clk = at91_clk_register_main(pmc, irq, name, parent_name, rate);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_main_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_main_setup(np, pmc);
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define MASTER_SOURCE_MAX 4
|
||||
|
||||
#define MASTER_PRES_MASK 0x7
|
||||
#define MASTER_PRES_MAX MASTER_PRES_MASK
|
||||
#define MASTER_DIV_SHIFT 8
|
||||
#define MASTER_DIV_MASK 0x3
|
||||
|
||||
struct clk_master_characteristics {
|
||||
struct clk_range output;
|
||||
u32 divisors[4];
|
||||
u8 have_div3_pres;
|
||||
};
|
||||
|
||||
struct clk_master_layout {
|
||||
u32 mask;
|
||||
u8 pres_shift;
|
||||
};
|
||||
|
||||
#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
|
||||
|
||||
struct clk_master {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
const struct clk_master_layout *layout;
|
||||
const struct clk_master_characteristics *characteristics;
|
||||
};
|
||||
|
||||
static irqreturn_t clk_master_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_master *master = (struct clk_master *)dev_id;
|
||||
|
||||
wake_up(&master->wait);
|
||||
disable_irq_nosync(master->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static int clk_master_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_master *master = to_clk_master(hw);
|
||||
struct at91_pmc *pmc = master->pmc;
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY)) {
|
||||
enable_irq(master->irq);
|
||||
wait_event(master->wait,
|
||||
pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_master_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_master *master = to_clk_master(hw);
|
||||
|
||||
return !!(pmc_read(master->pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY);
|
||||
}
|
||||
|
||||
static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 pres;
|
||||
u8 div;
|
||||
unsigned long rate = parent_rate;
|
||||
struct clk_master *master = to_clk_master(hw);
|
||||
struct at91_pmc *pmc = master->pmc;
|
||||
const struct clk_master_layout *layout = master->layout;
|
||||
const struct clk_master_characteristics *characteristics =
|
||||
master->characteristics;
|
||||
u32 tmp;
|
||||
|
||||
pmc_lock(pmc);
|
||||
tmp = pmc_read(pmc, AT91_PMC_MCKR) & layout->mask;
|
||||
pmc_unlock(pmc);
|
||||
|
||||
pres = (tmp >> layout->pres_shift) & MASTER_PRES_MASK;
|
||||
div = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
|
||||
|
||||
if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
|
||||
rate /= 3;
|
||||
else
|
||||
rate >>= pres;
|
||||
|
||||
rate /= characteristics->divisors[div];
|
||||
|
||||
if (rate < characteristics->output.min)
|
||||
pr_warn("master clk is underclocked");
|
||||
else if (rate > characteristics->output.max)
|
||||
pr_warn("master clk is overclocked");
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static u8 clk_master_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_master *master = to_clk_master(hw);
|
||||
struct at91_pmc *pmc = master->pmc;
|
||||
|
||||
return pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_CSS;
|
||||
}
|
||||
|
||||
static const struct clk_ops master_ops = {
|
||||
.prepare = clk_master_prepare,
|
||||
.is_prepared = clk_master_is_prepared,
|
||||
.recalc_rate = clk_master_recalc_rate,
|
||||
.get_parent = clk_master_get_parent,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_master(struct at91_pmc *pmc, unsigned int irq,
|
||||
const char *name, int num_parents,
|
||||
const char **parent_names,
|
||||
const struct clk_master_layout *layout,
|
||||
const struct clk_master_characteristics *characteristics)
|
||||
{
|
||||
int ret;
|
||||
struct clk_master *master;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
if (!pmc || !irq || !name || !num_parents || !parent_names)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
master = kzalloc(sizeof(*master), GFP_KERNEL);
|
||||
if (!master)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &master_ops;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
init.flags = 0;
|
||||
|
||||
master->hw.init = &init;
|
||||
master->layout = layout;
|
||||
master->characteristics = characteristics;
|
||||
master->pmc = pmc;
|
||||
master->irq = irq;
|
||||
init_waitqueue_head(&master->wait);
|
||||
irq_set_status_flags(master->irq, IRQ_NOAUTOEN);
|
||||
ret = request_irq(master->irq, clk_master_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, "clk-master", master);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &master->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(master);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
||||
static const struct clk_master_layout at91rm9200_master_layout = {
|
||||
.mask = 0x31F,
|
||||
.pres_shift = 2,
|
||||
};
|
||||
|
||||
static const struct clk_master_layout at91sam9x5_master_layout = {
|
||||
.mask = 0x373,
|
||||
.pres_shift = 4,
|
||||
};
|
||||
|
||||
|
||||
static struct clk_master_characteristics * __init
|
||||
of_at91_clk_master_get_characteristics(struct device_node *np)
|
||||
{
|
||||
struct clk_master_characteristics *characteristics;
|
||||
|
||||
characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
|
||||
if (!characteristics)
|
||||
return NULL;
|
||||
|
||||
if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
|
||||
goto out_free_characteristics;
|
||||
|
||||
of_property_read_u32_array(np, "atmel,clk-divisors",
|
||||
characteristics->divisors, 4);
|
||||
|
||||
characteristics->have_div3_pres =
|
||||
of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
|
||||
|
||||
return characteristics;
|
||||
|
||||
out_free_characteristics:
|
||||
kfree(characteristics);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc,
|
||||
const struct clk_master_layout *layout)
|
||||
{
|
||||
struct clk *clk;
|
||||
int num_parents;
|
||||
int i;
|
||||
unsigned int irq;
|
||||
const char *parent_names[MASTER_SOURCE_MAX];
|
||||
const char *name = np->name;
|
||||
struct clk_master_characteristics *characteristics;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_parents; ++i) {
|
||||
parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
if (!parent_names[i])
|
||||
return;
|
||||
}
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
characteristics = of_at91_clk_master_get_characteristics(np);
|
||||
if (!characteristics)
|
||||
return;
|
||||
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
clk = at91_clk_register_master(pmc, irq, name, num_parents,
|
||||
parent_names, layout,
|
||||
characteristics);
|
||||
if (IS_ERR(clk))
|
||||
goto out_free_characteristics;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
return;
|
||||
|
||||
out_free_characteristics:
|
||||
kfree(characteristics);
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_master_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_master_setup(np, pmc, &at91rm9200_master_layout);
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_master_setup(np, pmc, &at91sam9x5_master_layout);
|
||||
}
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define PERIPHERAL_MAX 64
|
||||
|
||||
#define PERIPHERAL_AT91RM9200 0
|
||||
#define PERIPHERAL_AT91SAM9X5 1
|
||||
|
||||
#define PERIPHERAL_ID_MIN 2
|
||||
#define PERIPHERAL_ID_MAX 31
|
||||
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
|
||||
|
||||
#define PERIPHERAL_RSHIFT_MASK 0x3
|
||||
#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
|
||||
|
||||
#define PERIPHERAL_MAX_SHIFT 4
|
||||
|
||||
struct clk_peripheral {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
|
||||
|
||||
struct clk_sam9x5_peripheral {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
struct clk_range range;
|
||||
u32 id;
|
||||
u32 div;
|
||||
bool auto_div;
|
||||
};
|
||||
|
||||
#define to_clk_sam9x5_peripheral(hw) \
|
||||
container_of(hw, struct clk_sam9x5_peripheral, hw)
|
||||
|
||||
static int clk_peripheral_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_peripheral *periph = to_clk_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
int offset = AT91_PMC_PCER;
|
||||
u32 id = periph->id;
|
||||
|
||||
if (id < PERIPHERAL_ID_MIN)
|
||||
return 0;
|
||||
if (id > PERIPHERAL_ID_MAX)
|
||||
offset = AT91_PMC_PCER1;
|
||||
pmc_write(pmc, offset, PERIPHERAL_MASK(id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_peripheral_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_peripheral *periph = to_clk_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
int offset = AT91_PMC_PCDR;
|
||||
u32 id = periph->id;
|
||||
|
||||
if (id < PERIPHERAL_ID_MIN)
|
||||
return;
|
||||
if (id > PERIPHERAL_ID_MAX)
|
||||
offset = AT91_PMC_PCDR1;
|
||||
pmc_write(pmc, offset, PERIPHERAL_MASK(id));
|
||||
}
|
||||
|
||||
static int clk_peripheral_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_peripheral *periph = to_clk_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
int offset = AT91_PMC_PCSR;
|
||||
u32 id = periph->id;
|
||||
|
||||
if (id < PERIPHERAL_ID_MIN)
|
||||
return 1;
|
||||
if (id > PERIPHERAL_ID_MAX)
|
||||
offset = AT91_PMC_PCSR1;
|
||||
return !!(pmc_read(pmc, offset) & PERIPHERAL_MASK(id));
|
||||
}
|
||||
|
||||
static const struct clk_ops peripheral_ops = {
|
||||
.enable = clk_peripheral_enable,
|
||||
.disable = clk_peripheral_disable,
|
||||
.is_enabled = clk_peripheral_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name, u32 id)
|
||||
{
|
||||
struct clk_peripheral *periph;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
if (!pmc || !name || !parent_name || id > PERIPHERAL_ID_MAX)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
|
||||
if (!periph)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &peripheral_ops;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
init.flags = 0;
|
||||
|
||||
periph->id = id;
|
||||
periph->hw.init = &init;
|
||||
periph->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &periph->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(periph);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
|
||||
{
|
||||
struct clk *parent;
|
||||
unsigned long parent_rate;
|
||||
int shift = 0;
|
||||
|
||||
if (!periph->auto_div)
|
||||
return;
|
||||
|
||||
if (periph->range.max) {
|
||||
parent = clk_get_parent_by_index(periph->hw.clk, 0);
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
if (!parent_rate)
|
||||
return;
|
||||
|
||||
for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
|
||||
if (parent_rate >> shift <= periph->range.max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
periph->auto_div = false;
|
||||
periph->div = shift;
|
||||
}
|
||||
|
||||
static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
|
||||
if (periph->id < PERIPHERAL_ID_MIN)
|
||||
return 0;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
|
||||
AT91_PMC_PCR_CMD |
|
||||
AT91_PMC_PCR_DIV(periph->div) |
|
||||
AT91_PMC_PCR_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
|
||||
if (periph->id < PERIPHERAL_ID_MIN)
|
||||
return;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
|
||||
AT91_PMC_PCR_CMD);
|
||||
}
|
||||
|
||||
static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
int ret;
|
||||
|
||||
if (periph->id < PERIPHERAL_ID_MIN)
|
||||
return 1;
|
||||
|
||||
pmc_lock(pmc);
|
||||
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
|
||||
ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN);
|
||||
pmc_unlock(pmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
struct at91_pmc *pmc = periph->pmc;
|
||||
u32 tmp;
|
||||
|
||||
if (periph->id < PERIPHERAL_ID_MIN)
|
||||
return parent_rate;
|
||||
|
||||
pmc_lock(pmc);
|
||||
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
|
||||
tmp = pmc_read(pmc, AT91_PMC_PCR);
|
||||
pmc_unlock(pmc);
|
||||
|
||||
if (tmp & AT91_PMC_PCR_EN) {
|
||||
periph->div = PERIPHERAL_RSHIFT(tmp);
|
||||
periph->auto_div = false;
|
||||
} else {
|
||||
clk_sam9x5_peripheral_autodiv(periph);
|
||||
}
|
||||
|
||||
return parent_rate >> periph->div;
|
||||
}
|
||||
|
||||
static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
int shift = 0;
|
||||
unsigned long best_rate;
|
||||
unsigned long best_diff;
|
||||
unsigned long cur_rate = *parent_rate;
|
||||
unsigned long cur_diff;
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
|
||||
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
|
||||
return *parent_rate;
|
||||
|
||||
if (periph->range.max) {
|
||||
for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
|
||||
cur_rate = *parent_rate >> shift;
|
||||
if (cur_rate <= periph->range.max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rate >= cur_rate)
|
||||
return cur_rate;
|
||||
|
||||
best_diff = cur_rate - rate;
|
||||
best_rate = cur_rate;
|
||||
for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
|
||||
cur_rate = *parent_rate >> shift;
|
||||
if (cur_rate < rate)
|
||||
cur_diff = rate - cur_rate;
|
||||
else
|
||||
cur_diff = cur_rate - rate;
|
||||
|
||||
if (cur_diff < best_diff) {
|
||||
best_diff = cur_diff;
|
||||
best_rate = cur_rate;
|
||||
}
|
||||
|
||||
if (!best_diff || cur_rate < rate)
|
||||
break;
|
||||
}
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int shift;
|
||||
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
|
||||
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
|
||||
if (parent_rate == rate)
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (periph->range.max && rate > periph->range.max)
|
||||
return -EINVAL;
|
||||
|
||||
for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) {
|
||||
if (parent_rate >> shift == rate) {
|
||||
periph->auto_div = false;
|
||||
periph->div = shift;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct clk_ops sam9x5_peripheral_ops = {
|
||||
.enable = clk_sam9x5_peripheral_enable,
|
||||
.disable = clk_sam9x5_peripheral_disable,
|
||||
.is_enabled = clk_sam9x5_peripheral_is_enabled,
|
||||
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
|
||||
.round_rate = clk_sam9x5_peripheral_round_rate,
|
||||
.set_rate = clk_sam9x5_peripheral_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name, u32 id,
|
||||
const struct clk_range *range)
|
||||
{
|
||||
struct clk_sam9x5_peripheral *periph;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
if (!pmc || !name || !parent_name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
|
||||
if (!periph)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &sam9x5_peripheral_ops;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
init.flags = 0;
|
||||
|
||||
periph->id = id;
|
||||
periph->hw.init = &init;
|
||||
periph->div = 0;
|
||||
periph->pmc = pmc;
|
||||
periph->auto_div = true;
|
||||
periph->range = *range;
|
||||
|
||||
clk = clk_register(NULL, &periph->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(periph);
|
||||
else
|
||||
clk_sam9x5_peripheral_autodiv(periph);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type)
|
||||
{
|
||||
int num;
|
||||
u32 id;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name;
|
||||
struct device_node *periphclknp;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
num = of_get_child_count(np);
|
||||
if (!num || num > PERIPHERAL_MAX)
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, periphclknp) {
|
||||
if (of_property_read_u32(periphclknp, "reg", &id))
|
||||
continue;
|
||||
|
||||
if (id >= PERIPHERAL_MAX)
|
||||
continue;
|
||||
|
||||
if (of_property_read_string(np, "clock-output-names", &name))
|
||||
name = periphclknp->name;
|
||||
|
||||
if (type == PERIPHERAL_AT91RM9200) {
|
||||
clk = at91_clk_register_peripheral(pmc, name,
|
||||
parent_name, id);
|
||||
} else {
|
||||
struct clk_range range = CLK_RANGE(0, 0);
|
||||
|
||||
of_at91_get_clk_range(periphclknp,
|
||||
"atmel,clk-output-range",
|
||||
&range);
|
||||
|
||||
clk = at91_clk_register_sam9x5_peripheral(pmc, name,
|
||||
parent_name,
|
||||
id, &range);
|
||||
}
|
||||
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
|
||||
}
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200);
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5);
|
||||
}
|
|
@ -0,0 +1,531 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define PLL_STATUS_MASK(id) (1 << (1 + (id)))
|
||||
#define PLL_REG(id) (AT91_CKGR_PLLAR + ((id) * 4))
|
||||
#define PLL_DIV_MASK 0xff
|
||||
#define PLL_DIV_MAX PLL_DIV_MASK
|
||||
#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK)
|
||||
#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \
|
||||
(layout)->mul_mask)
|
||||
#define PLL_ICPR_SHIFT(id) ((id) * 16)
|
||||
#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id))
|
||||
#define PLL_MAX_COUNT 0x3ff
|
||||
#define PLL_COUNT_SHIFT 8
|
||||
#define PLL_OUT_SHIFT 14
|
||||
#define PLL_MAX_ID 1
|
||||
|
||||
struct clk_pll_characteristics {
|
||||
struct clk_range input;
|
||||
int num_output;
|
||||
struct clk_range *output;
|
||||
u16 *icpll;
|
||||
u8 *out;
|
||||
};
|
||||
|
||||
struct clk_pll_layout {
|
||||
u32 pllr_mask;
|
||||
u16 mul_mask;
|
||||
u8 mul_shift;
|
||||
};
|
||||
|
||||
#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw)
|
||||
|
||||
struct clk_pll {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
u8 id;
|
||||
u8 div;
|
||||
u8 range;
|
||||
u16 mul;
|
||||
const struct clk_pll_layout *layout;
|
||||
const struct clk_pll_characteristics *characteristics;
|
||||
};
|
||||
|
||||
static irqreturn_t clk_pll_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_pll *pll = (struct clk_pll *)dev_id;
|
||||
|
||||
wake_up(&pll->wait);
|
||||
disable_irq_nosync(pll->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
struct at91_pmc *pmc = pll->pmc;
|
||||
const struct clk_pll_layout *layout = pll->layout;
|
||||
const struct clk_pll_characteristics *characteristics =
|
||||
pll->characteristics;
|
||||
u8 id = pll->id;
|
||||
u32 mask = PLL_STATUS_MASK(id);
|
||||
int offset = PLL_REG(id);
|
||||
u8 out = 0;
|
||||
u32 pllr, icpr;
|
||||
u8 div;
|
||||
u16 mul;
|
||||
|
||||
pllr = pmc_read(pmc, offset);
|
||||
div = PLL_DIV(pllr);
|
||||
mul = PLL_MUL(pllr, layout);
|
||||
|
||||
if ((pmc_read(pmc, AT91_PMC_SR) & mask) &&
|
||||
(div == pll->div && mul == pll->mul))
|
||||
return 0;
|
||||
|
||||
if (characteristics->out)
|
||||
out = characteristics->out[pll->range];
|
||||
if (characteristics->icpll) {
|
||||
icpr = pmc_read(pmc, AT91_PMC_PLLICPR) & ~PLL_ICPR_MASK(id);
|
||||
icpr |= (characteristics->icpll[pll->range] <<
|
||||
PLL_ICPR_SHIFT(id));
|
||||
pmc_write(pmc, AT91_PMC_PLLICPR, icpr);
|
||||
}
|
||||
|
||||
pllr &= ~layout->pllr_mask;
|
||||
pllr |= layout->pllr_mask &
|
||||
(pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
|
||||
(out << PLL_OUT_SHIFT) |
|
||||
((pll->mul & layout->mul_mask) << layout->mul_shift));
|
||||
pmc_write(pmc, offset, pllr);
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) {
|
||||
enable_irq(pll->irq);
|
||||
wait_event(pll->wait,
|
||||
pmc_read(pmc, AT91_PMC_SR) & mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
struct at91_pmc *pmc = pll->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SR) &
|
||||
PLL_STATUS_MASK(pll->id));
|
||||
}
|
||||
|
||||
static void clk_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
struct at91_pmc *pmc = pll->pmc;
|
||||
const struct clk_pll_layout *layout = pll->layout;
|
||||
int offset = PLL_REG(pll->id);
|
||||
u32 tmp = pmc_read(pmc, offset) & ~(layout->pllr_mask);
|
||||
|
||||
pmc_write(pmc, offset, tmp);
|
||||
}
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
const struct clk_pll_layout *layout = pll->layout;
|
||||
struct at91_pmc *pmc = pll->pmc;
|
||||
int offset = PLL_REG(pll->id);
|
||||
u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask;
|
||||
u8 div = PLL_DIV(tmp);
|
||||
u16 mul = PLL_MUL(tmp, layout);
|
||||
if (!div || !mul)
|
||||
return 0;
|
||||
|
||||
return (parent_rate * (mul + 1)) / div;
|
||||
}
|
||||
|
||||
static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u32 *div, u32 *mul,
|
||||
u32 *index) {
|
||||
unsigned long maxrate;
|
||||
unsigned long minrate;
|
||||
unsigned long divrate;
|
||||
unsigned long bestdiv = 1;
|
||||
unsigned long bestmul;
|
||||
unsigned long tmpdiv;
|
||||
unsigned long roundup;
|
||||
unsigned long rounddown;
|
||||
unsigned long remainder;
|
||||
unsigned long bestremainder;
|
||||
unsigned long maxmul;
|
||||
unsigned long maxdiv;
|
||||
unsigned long mindiv;
|
||||
int i = 0;
|
||||
const struct clk_pll_layout *layout = pll->layout;
|
||||
const struct clk_pll_characteristics *characteristics =
|
||||
pll->characteristics;
|
||||
|
||||
/* Minimum divider = 1 */
|
||||
/* Maximum multiplier = max_mul */
|
||||
maxmul = layout->mul_mask + 1;
|
||||
maxrate = (parent_rate * maxmul) / 1;
|
||||
|
||||
/* Maximum divider = max_div */
|
||||
/* Minimum multiplier = 2 */
|
||||
maxdiv = PLL_DIV_MAX;
|
||||
minrate = (parent_rate * 2) / maxdiv;
|
||||
|
||||
if (parent_rate < characteristics->input.min ||
|
||||
parent_rate < characteristics->input.max)
|
||||
return -ERANGE;
|
||||
|
||||
if (parent_rate < minrate || parent_rate > maxrate)
|
||||
return -ERANGE;
|
||||
|
||||
for (i = 0; i < characteristics->num_output; i++) {
|
||||
if (parent_rate >= characteristics->output[i].min &&
|
||||
parent_rate <= characteristics->output[i].max)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= characteristics->num_output)
|
||||
return -ERANGE;
|
||||
|
||||
bestmul = rate / parent_rate;
|
||||
rounddown = parent_rate % rate;
|
||||
roundup = rate - rounddown;
|
||||
bestremainder = roundup < rounddown ? roundup : rounddown;
|
||||
|
||||
if (!bestremainder) {
|
||||
if (div)
|
||||
*div = bestdiv;
|
||||
if (mul)
|
||||
*mul = bestmul;
|
||||
if (index)
|
||||
*index = i;
|
||||
return rate;
|
||||
}
|
||||
|
||||
maxdiv = 255 / (bestmul + 1);
|
||||
if (parent_rate / maxdiv < characteristics->input.min)
|
||||
maxdiv = parent_rate / characteristics->input.min;
|
||||
mindiv = parent_rate / characteristics->input.max;
|
||||
if (parent_rate % characteristics->input.max)
|
||||
mindiv++;
|
||||
|
||||
for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) {
|
||||
divrate = parent_rate / tmpdiv;
|
||||
|
||||
rounddown = rate % divrate;
|
||||
roundup = divrate - rounddown;
|
||||
remainder = roundup < rounddown ? roundup : rounddown;
|
||||
|
||||
if (remainder < bestremainder) {
|
||||
bestremainder = remainder;
|
||||
bestmul = rate / divrate;
|
||||
bestdiv = tmpdiv;
|
||||
}
|
||||
|
||||
if (!remainder)
|
||||
break;
|
||||
}
|
||||
|
||||
rate = (parent_rate / bestdiv) * bestmul;
|
||||
|
||||
if (div)
|
||||
*div = bestdiv;
|
||||
if (mul)
|
||||
*mul = bestmul;
|
||||
if (index)
|
||||
*index = i;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
|
||||
return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
long ret;
|
||||
u32 div;
|
||||
u32 mul;
|
||||
u32 index;
|
||||
|
||||
ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
|
||||
&div, &mul, &index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pll->range = index;
|
||||
pll->div = div;
|
||||
pll->mul = mul;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops pll_ops = {
|
||||
.prepare = clk_pll_prepare,
|
||||
.unprepare = clk_pll_unprepare,
|
||||
.is_prepared = clk_pll_is_prepared,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_round_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_pll(struct at91_pmc *pmc, unsigned int irq, const char *name,
|
||||
const char *parent_name, u8 id,
|
||||
const struct clk_pll_layout *layout,
|
||||
const struct clk_pll_characteristics *characteristics)
|
||||
{
|
||||
struct clk_pll *pll;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
int offset = PLL_REG(id);
|
||||
u32 tmp;
|
||||
|
||||
if (id > PLL_MAX_ID)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &pll_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
pll->id = id;
|
||||
pll->hw.init = &init;
|
||||
pll->layout = layout;
|
||||
pll->characteristics = characteristics;
|
||||
pll->pmc = pmc;
|
||||
pll->irq = irq;
|
||||
tmp = pmc_read(pmc, offset) & layout->pllr_mask;
|
||||
pll->div = PLL_DIV(tmp);
|
||||
pll->mul = PLL_MUL(tmp, layout);
|
||||
init_waitqueue_head(&pll->wait);
|
||||
irq_set_status_flags(pll->irq, IRQ_NOAUTOEN);
|
||||
ret = request_irq(pll->irq, clk_pll_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
id ? "clk-pllb" : "clk-plla", pll);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &pll->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(pll);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
||||
static const struct clk_pll_layout at91rm9200_pll_layout = {
|
||||
.pllr_mask = 0x7FFFFFF,
|
||||
.mul_shift = 16,
|
||||
.mul_mask = 0x7FF,
|
||||
};
|
||||
|
||||
static const struct clk_pll_layout at91sam9g45_pll_layout = {
|
||||
.pllr_mask = 0xFFFFFF,
|
||||
.mul_shift = 16,
|
||||
.mul_mask = 0xFF,
|
||||
};
|
||||
|
||||
static const struct clk_pll_layout at91sam9g20_pllb_layout = {
|
||||
.pllr_mask = 0x3FFFFF,
|
||||
.mul_shift = 16,
|
||||
.mul_mask = 0x3F,
|
||||
};
|
||||
|
||||
static const struct clk_pll_layout sama5d3_pll_layout = {
|
||||
.pllr_mask = 0x1FFFFFF,
|
||||
.mul_shift = 18,
|
||||
.mul_mask = 0x7F,
|
||||
};
|
||||
|
||||
|
||||
static struct clk_pll_characteristics * __init
|
||||
of_at91_clk_pll_get_characteristics(struct device_node *np)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
u32 tmp;
|
||||
int num_output;
|
||||
u32 num_cells;
|
||||
struct clk_range input;
|
||||
struct clk_range *output;
|
||||
u8 *out = NULL;
|
||||
u16 *icpll = NULL;
|
||||
struct clk_pll_characteristics *characteristics;
|
||||
|
||||
if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
|
||||
return NULL;
|
||||
|
||||
if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
|
||||
&num_cells))
|
||||
return NULL;
|
||||
|
||||
if (num_cells < 2 || num_cells > 4)
|
||||
return NULL;
|
||||
|
||||
if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
|
||||
return NULL;
|
||||
num_output = tmp / (sizeof(u32) * num_cells);
|
||||
|
||||
characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
|
||||
if (!characteristics)
|
||||
return NULL;
|
||||
|
||||
output = kzalloc(sizeof(*output) * num_output, GFP_KERNEL);
|
||||
if (!output)
|
||||
goto out_free_characteristics;
|
||||
|
||||
if (num_cells > 2) {
|
||||
out = kzalloc(sizeof(*out) * num_output, GFP_KERNEL);
|
||||
if (!out)
|
||||
goto out_free_output;
|
||||
}
|
||||
|
||||
if (num_cells > 3) {
|
||||
icpll = kzalloc(sizeof(*icpll) * num_output, GFP_KERNEL);
|
||||
if (!icpll)
|
||||
goto out_free_output;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_output; i++) {
|
||||
offset = i * num_cells;
|
||||
if (of_property_read_u32_index(np,
|
||||
"atmel,pll-clk-output-ranges",
|
||||
offset, &tmp))
|
||||
goto out_free_output;
|
||||
output[i].min = tmp;
|
||||
if (of_property_read_u32_index(np,
|
||||
"atmel,pll-clk-output-ranges",
|
||||
offset + 1, &tmp))
|
||||
goto out_free_output;
|
||||
output[i].max = tmp;
|
||||
|
||||
if (num_cells == 2)
|
||||
continue;
|
||||
|
||||
if (of_property_read_u32_index(np,
|
||||
"atmel,pll-clk-output-ranges",
|
||||
offset + 2, &tmp))
|
||||
goto out_free_output;
|
||||
out[i] = tmp;
|
||||
|
||||
if (num_cells == 3)
|
||||
continue;
|
||||
|
||||
if (of_property_read_u32_index(np,
|
||||
"atmel,pll-clk-output-ranges",
|
||||
offset + 3, &tmp))
|
||||
goto out_free_output;
|
||||
icpll[i] = tmp;
|
||||
}
|
||||
|
||||
characteristics->input = input;
|
||||
characteristics->num_output = num_output;
|
||||
characteristics->output = output;
|
||||
characteristics->out = out;
|
||||
characteristics->icpll = icpll;
|
||||
return characteristics;
|
||||
|
||||
out_free_output:
|
||||
kfree(icpll);
|
||||
kfree(out);
|
||||
kfree(output);
|
||||
out_free_characteristics:
|
||||
kfree(characteristics);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_pll_setup(struct device_node *np, struct at91_pmc *pmc,
|
||||
const struct clk_pll_layout *layout)
|
||||
{
|
||||
u32 id;
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
struct clk_pll_characteristics *characteristics;
|
||||
|
||||
if (of_property_read_u32(np, "reg", &id))
|
||||
return;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
characteristics = of_at91_clk_pll_get_characteristics(np);
|
||||
if (!characteristics)
|
||||
return;
|
||||
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
clk = at91_clk_register_pll(pmc, irq, name, parent_name, id, layout,
|
||||
characteristics);
|
||||
if (IS_ERR(clk))
|
||||
goto out_free_characteristics;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
return;
|
||||
|
||||
out_free_characteristics:
|
||||
kfree(characteristics);
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_pll_setup(np, pmc, &at91rm9200_pll_layout);
|
||||
}
|
||||
|
||||
void __init of_at91sam9g45_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_pll_setup(np, pmc, &at91sam9g45_pll_layout);
|
||||
}
|
||||
|
||||
void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_pll_setup(np, pmc, &at91sam9g20_pllb_layout);
|
||||
}
|
||||
|
||||
void __init of_sama5d3_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_pll_setup(np, pmc, &sama5d3_pll_layout);
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
|
||||
|
||||
struct clk_plldiv {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
};
|
||||
|
||||
static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_plldiv *plldiv = to_clk_plldiv(hw);
|
||||
struct at91_pmc *pmc = plldiv->pmc;
|
||||
|
||||
if (pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_PLLADIV2)
|
||||
return parent_rate / 2;
|
||||
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long div;
|
||||
|
||||
if (rate > *parent_rate)
|
||||
return *parent_rate;
|
||||
div = *parent_rate / 2;
|
||||
if (rate < div)
|
||||
return div;
|
||||
|
||||
if (rate - div < *parent_rate - rate)
|
||||
return div;
|
||||
|
||||
return *parent_rate;
|
||||
}
|
||||
|
||||
static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_plldiv *plldiv = to_clk_plldiv(hw);
|
||||
struct at91_pmc *pmc = plldiv->pmc;
|
||||
u32 tmp;
|
||||
|
||||
if (parent_rate != rate && (parent_rate / 2) != rate)
|
||||
return -EINVAL;
|
||||
|
||||
pmc_lock(pmc);
|
||||
tmp = pmc_read(pmc, AT91_PMC_MCKR) & ~AT91_PMC_PLLADIV2;
|
||||
if ((parent_rate / 2) == rate)
|
||||
tmp |= AT91_PMC_PLLADIV2;
|
||||
pmc_write(pmc, AT91_PMC_MCKR, tmp);
|
||||
pmc_unlock(pmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops plldiv_ops = {
|
||||
.recalc_rate = clk_plldiv_recalc_rate,
|
||||
.round_rate = clk_plldiv_round_rate,
|
||||
.set_rate = clk_plldiv_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_plldiv(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name)
|
||||
{
|
||||
struct clk_plldiv *plldiv;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
|
||||
if (!plldiv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &plldiv_ops;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
plldiv->hw.init = &init;
|
||||
plldiv->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &plldiv->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(plldiv);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_plldiv_setup(struct device_node *np, struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
clk = at91_clk_register_plldiv(pmc, name, parent_name);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
return;
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_plldiv_setup(np, pmc);
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define PROG_SOURCE_MAX 5
|
||||
#define PROG_ID_MAX 7
|
||||
|
||||
#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
|
||||
#define PROG_PRES_MASK 0x7
|
||||
#define PROG_MAX_RM9200_CSS 3
|
||||
|
||||
struct clk_programmable_layout {
|
||||
u8 pres_shift;
|
||||
u8 css_mask;
|
||||
u8 have_slck_mck;
|
||||
};
|
||||
|
||||
struct clk_programmable {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
u8 id;
|
||||
u8 css;
|
||||
u8 pres;
|
||||
u8 slckmck;
|
||||
const struct clk_programmable_layout *layout;
|
||||
};
|
||||
|
||||
#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
|
||||
|
||||
|
||||
static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_programmable *prog = (struct clk_programmable *)dev_id;
|
||||
|
||||
wake_up(&prog->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_programmable_prepare(struct clk_hw *hw)
|
||||
{
|
||||
u32 tmp;
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
u8 id = prog->id;
|
||||
u32 mask = PROG_STATUS_MASK(id);
|
||||
|
||||
tmp = prog->css | (prog->pres << layout->pres_shift);
|
||||
if (layout->have_slck_mck && prog->slckmck)
|
||||
tmp |= AT91_PMC_CSSMCK_MCK;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_PCKR(id), tmp);
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & mask))
|
||||
wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_programmable_is_ready(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id));
|
||||
}
|
||||
|
||||
static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
|
||||
prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
|
||||
|
||||
return parent_rate >> prog->pres;
|
||||
}
|
||||
|
||||
static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long best_rate = *parent_rate;
|
||||
unsigned long best_diff;
|
||||
unsigned long new_diff;
|
||||
unsigned long cur_rate;
|
||||
int shift = shift;
|
||||
|
||||
if (rate > *parent_rate)
|
||||
return *parent_rate;
|
||||
else
|
||||
best_diff = *parent_rate - rate;
|
||||
|
||||
if (!best_diff)
|
||||
return best_rate;
|
||||
|
||||
for (shift = 1; shift < PROG_PRES_MASK; shift++) {
|
||||
cur_rate = *parent_rate >> shift;
|
||||
|
||||
if (cur_rate > rate)
|
||||
new_diff = cur_rate - rate;
|
||||
else
|
||||
new_diff = rate - cur_rate;
|
||||
|
||||
if (!new_diff)
|
||||
return cur_rate;
|
||||
|
||||
if (new_diff < best_diff) {
|
||||
best_diff = new_diff;
|
||||
best_rate = cur_rate;
|
||||
}
|
||||
|
||||
if (rate > cur_rate)
|
||||
break;
|
||||
}
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
if (index > layout->css_mask) {
|
||||
if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) {
|
||||
prog->css = 0;
|
||||
prog->slckmck = 1;
|
||||
return 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
prog->css = index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 clk_programmable_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
u32 tmp;
|
||||
u8 ret;
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
|
||||
prog->css = tmp & layout->css_mask;
|
||||
ret = prog->css;
|
||||
if (layout->have_slck_mck) {
|
||||
prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK);
|
||||
if (prog->slckmck && !ret)
|
||||
ret = PROG_MAX_RM9200_CSS + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
unsigned long best_rate = parent_rate;
|
||||
unsigned long best_diff;
|
||||
unsigned long new_diff;
|
||||
unsigned long cur_rate;
|
||||
int shift = 0;
|
||||
|
||||
if (rate > parent_rate)
|
||||
return parent_rate;
|
||||
else
|
||||
best_diff = parent_rate - rate;
|
||||
|
||||
if (!best_diff) {
|
||||
prog->pres = shift;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (shift = 1; shift < PROG_PRES_MASK; shift++) {
|
||||
cur_rate = parent_rate >> shift;
|
||||
|
||||
if (cur_rate > rate)
|
||||
new_diff = cur_rate - rate;
|
||||
else
|
||||
new_diff = rate - cur_rate;
|
||||
|
||||
if (!new_diff)
|
||||
break;
|
||||
|
||||
if (new_diff < best_diff) {
|
||||
best_diff = new_diff;
|
||||
best_rate = cur_rate;
|
||||
}
|
||||
|
||||
if (rate > cur_rate)
|
||||
break;
|
||||
}
|
||||
|
||||
prog->pres = shift;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops programmable_ops = {
|
||||
.prepare = clk_programmable_prepare,
|
||||
.is_prepared = clk_programmable_is_ready,
|
||||
.recalc_rate = clk_programmable_recalc_rate,
|
||||
.round_rate = clk_programmable_round_rate,
|
||||
.get_parent = clk_programmable_get_parent,
|
||||
.set_parent = clk_programmable_set_parent,
|
||||
.set_rate = clk_programmable_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
|
||||
const char *name, const char **parent_names,
|
||||
u8 num_parents, u8 id,
|
||||
const struct clk_programmable_layout *layout)
|
||||
{
|
||||
int ret;
|
||||
struct clk_programmable *prog;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
char irq_name[11];
|
||||
|
||||
if (id > PROG_ID_MAX)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
prog = kzalloc(sizeof(*prog), GFP_KERNEL);
|
||||
if (!prog)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &programmable_ops;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
|
||||
|
||||
prog->id = id;
|
||||
prog->layout = layout;
|
||||
prog->hw.init = &init;
|
||||
prog->pmc = pmc;
|
||||
prog->irq = irq;
|
||||
init_waitqueue_head(&prog->wait);
|
||||
irq_set_status_flags(prog->irq, IRQ_NOAUTOEN);
|
||||
snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id);
|
||||
ret = request_irq(prog->irq, clk_programmable_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, irq_name, prog);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &prog->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(prog);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static const struct clk_programmable_layout at91rm9200_programmable_layout = {
|
||||
.pres_shift = 2,
|
||||
.css_mask = 0x3,
|
||||
.have_slck_mck = 0,
|
||||
};
|
||||
|
||||
static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
|
||||
.pres_shift = 2,
|
||||
.css_mask = 0x3,
|
||||
.have_slck_mck = 1,
|
||||
};
|
||||
|
||||
static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
|
||||
.pres_shift = 4,
|
||||
.css_mask = 0x7,
|
||||
.have_slck_mck = 0,
|
||||
};
|
||||
|
||||
static void __init
|
||||
of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
|
||||
const struct clk_programmable_layout *layout)
|
||||
{
|
||||
int num;
|
||||
u32 id;
|
||||
int i;
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
int num_parents;
|
||||
const char *parent_names[PROG_SOURCE_MAX];
|
||||
const char *name;
|
||||
struct device_node *progclknp;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_parents; ++i) {
|
||||
parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
if (!parent_names[i])
|
||||
return;
|
||||
}
|
||||
|
||||
num = of_get_child_count(np);
|
||||
if (!num || num > (PROG_ID_MAX + 1))
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, progclknp) {
|
||||
if (of_property_read_u32(progclknp, "reg", &id))
|
||||
continue;
|
||||
|
||||
if (of_property_read_string(np, "clock-output-names", &name))
|
||||
name = progclknp->name;
|
||||
|
||||
irq = irq_of_parse_and_map(progclknp, 0);
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
clk = at91_clk_register_programmable(pmc, irq, name,
|
||||
parent_names, num_parents,
|
||||
id, layout);
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void __init of_at91rm9200_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_prog_setup(np, pmc, &at91rm9200_programmable_layout);
|
||||
}
|
||||
|
||||
void __init of_at91sam9g45_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_prog_setup(np, pmc, &at91sam9g45_programmable_layout);
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_prog_setup(np, pmc, &at91sam9x5_programmable_layout);
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define SMD_SOURCE_MAX 2
|
||||
|
||||
#define SMD_DIV_SHIFT 8
|
||||
#define SMD_MAX_DIV 0xf
|
||||
|
||||
struct at91sam9x5_clk_smd {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
};
|
||||
|
||||
#define to_at91sam9x5_clk_smd(hw) \
|
||||
container_of(hw, struct at91sam9x5_clk_smd, hw)
|
||||
|
||||
static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
u8 smddiv;
|
||||
struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
|
||||
struct at91_pmc *pmc = smd->pmc;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_SMD);
|
||||
smddiv = (tmp & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT;
|
||||
return parent_rate / (smddiv + 1);
|
||||
}
|
||||
|
||||
static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long div;
|
||||
unsigned long bestrate;
|
||||
unsigned long tmp;
|
||||
|
||||
if (rate >= *parent_rate)
|
||||
return *parent_rate;
|
||||
|
||||
div = *parent_rate / rate;
|
||||
if (div > SMD_MAX_DIV)
|
||||
return *parent_rate / (SMD_MAX_DIV + 1);
|
||||
|
||||
bestrate = *parent_rate / div;
|
||||
tmp = *parent_rate / (div + 1);
|
||||
if (bestrate - rate > rate - tmp)
|
||||
bestrate = tmp;
|
||||
|
||||
return bestrate;
|
||||
}
|
||||
|
||||
static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
u32 tmp;
|
||||
struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
|
||||
struct at91_pmc *pmc = smd->pmc;
|
||||
|
||||
if (index > 1)
|
||||
return -EINVAL;
|
||||
tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMDS;
|
||||
if (index)
|
||||
tmp |= AT91_PMC_SMDS;
|
||||
pmc_write(pmc, AT91_PMC_SMD, tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 at91sam9x5_clk_smd_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
|
||||
struct at91_pmc *pmc = smd->pmc;
|
||||
|
||||
return pmc_read(pmc, AT91_PMC_SMD) & AT91_PMC_SMDS;
|
||||
}
|
||||
|
||||
static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
|
||||
struct at91_pmc *pmc = smd->pmc;
|
||||
unsigned long div = parent_rate / rate;
|
||||
|
||||
if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
|
||||
return -EINVAL;
|
||||
tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMD_DIV;
|
||||
tmp |= (div - 1) << SMD_DIV_SHIFT;
|
||||
pmc_write(pmc, AT91_PMC_SMD, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops at91sam9x5_smd_ops = {
|
||||
.recalc_rate = at91sam9x5_clk_smd_recalc_rate,
|
||||
.round_rate = at91sam9x5_clk_smd_round_rate,
|
||||
.get_parent = at91sam9x5_clk_smd_get_parent,
|
||||
.set_parent = at91sam9x5_clk_smd_set_parent,
|
||||
.set_rate = at91sam9x5_clk_smd_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91sam9x5_clk_register_smd(struct at91_pmc *pmc, const char *name,
|
||||
const char **parent_names, u8 num_parents)
|
||||
{
|
||||
struct at91sam9x5_clk_smd *smd;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
smd = kzalloc(sizeof(*smd), GFP_KERNEL);
|
||||
if (!smd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &at91sam9x5_smd_ops;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
|
||||
|
||||
smd->hw.init = &init;
|
||||
smd->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &smd->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(smd);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
int num_parents;
|
||||
const char *parent_names[SMD_SOURCE_MAX];
|
||||
const char *name = np->name;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_parents; i++) {
|
||||
parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
if (!parent_names[i])
|
||||
return;
|
||||
}
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
clk = at91sam9x5_clk_register_smd(pmc, name, parent_names,
|
||||
num_parents);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define SYSTEM_MAX_ID 31
|
||||
|
||||
#define SYSTEM_MAX_NAME_SZ 32
|
||||
|
||||
#define to_clk_system(hw) container_of(hw, struct clk_system, hw)
|
||||
struct clk_system {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static int clk_system_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_system_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id);
|
||||
}
|
||||
|
||||
static int clk_system_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id));
|
||||
}
|
||||
|
||||
static const struct clk_ops system_ops = {
|
||||
.enable = clk_system_enable,
|
||||
.disable = clk_system_disable,
|
||||
.is_enabled = clk_system_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_system(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name, u8 id)
|
||||
{
|
||||
struct clk_system *sys;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
if (!parent_name || id > SYSTEM_MAX_ID)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
sys = kzalloc(sizeof(*sys), GFP_KERNEL);
|
||||
if (!sys)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &system_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
/*
|
||||
* CLK_IGNORE_UNUSED is used to avoid ddrck switch off.
|
||||
* TODO : we should implement a driver supporting at91 ddr controller
|
||||
* (see drivers/memory) which would request and enable the ddrck clock.
|
||||
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
|
||||
*/
|
||||
init.flags = CLK_IGNORE_UNUSED;
|
||||
|
||||
sys->id = id;
|
||||
sys->hw.init = &init;
|
||||
sys->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &sys->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(sys);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
|
||||
{
|
||||
int num;
|
||||
u32 id;
|
||||
struct clk *clk;
|
||||
const char *name;
|
||||
struct device_node *sysclknp;
|
||||
const char *parent_name;
|
||||
|
||||
num = of_get_child_count(np);
|
||||
if (num > (SYSTEM_MAX_ID + 1))
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, sysclknp) {
|
||||
if (of_property_read_u32(sysclknp, "reg", &id))
|
||||
continue;
|
||||
|
||||
if (of_property_read_string(np, "clock-output-names", &name))
|
||||
name = sysclknp->name;
|
||||
|
||||
parent_name = of_clk_get_parent_name(sysclknp, 0);
|
||||
|
||||
clk = at91_clk_register_system(pmc, name, parent_name, id);
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
|
||||
}
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_sys_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_sys_setup(np, pmc);
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define USB_SOURCE_MAX 2
|
||||
|
||||
#define SAM9X5_USB_DIV_SHIFT 8
|
||||
#define SAM9X5_USB_MAX_DIV 0xf
|
||||
|
||||
#define RM9200_USB_DIV_SHIFT 28
|
||||
#define RM9200_USB_DIV_TAB_SIZE 4
|
||||
|
||||
struct at91sam9x5_clk_usb {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
};
|
||||
|
||||
#define to_at91sam9x5_clk_usb(hw) \
|
||||
container_of(hw, struct at91sam9x5_clk_usb, hw)
|
||||
|
||||
struct at91rm9200_clk_usb {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
u32 divisors[4];
|
||||
};
|
||||
|
||||
#define to_at91rm9200_clk_usb(hw) \
|
||||
container_of(hw, struct at91rm9200_clk_usb, hw)
|
||||
|
||||
static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
u8 usbdiv;
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_USB);
|
||||
usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
|
||||
return parent_rate / (usbdiv + 1);
|
||||
}
|
||||
|
||||
static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long div;
|
||||
unsigned long bestrate;
|
||||
unsigned long tmp;
|
||||
|
||||
if (rate >= *parent_rate)
|
||||
return *parent_rate;
|
||||
|
||||
div = *parent_rate / rate;
|
||||
if (div >= SAM9X5_USB_MAX_DIV)
|
||||
return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
|
||||
|
||||
bestrate = *parent_rate / div;
|
||||
tmp = *parent_rate / (div + 1);
|
||||
if (bestrate - rate > rate - tmp)
|
||||
bestrate = tmp;
|
||||
|
||||
return bestrate;
|
||||
}
|
||||
|
||||
static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
u32 tmp;
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
if (index > 1)
|
||||
return -EINVAL;
|
||||
tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS;
|
||||
if (index)
|
||||
tmp |= AT91_PMC_USBS;
|
||||
pmc_write(pmc, AT91_PMC_USB, tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
return pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS;
|
||||
}
|
||||
|
||||
static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
unsigned long div = parent_rate / rate;
|
||||
|
||||
if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
|
||||
tmp |= (div - 1) << SAM9X5_USB_DIV_SHIFT;
|
||||
pmc_write(pmc, AT91_PMC_USB, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops at91sam9x5_usb_ops = {
|
||||
.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
|
||||
.round_rate = at91sam9x5_clk_usb_round_rate,
|
||||
.get_parent = at91sam9x5_clk_usb_get_parent,
|
||||
.set_parent = at91sam9x5_clk_usb_set_parent,
|
||||
.set_rate = at91sam9x5_clk_usb_set_rate,
|
||||
};
|
||||
|
||||
static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_USB,
|
||||
pmc_read(pmc, AT91_PMC_USB) | AT91_PMC_USBS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_USB,
|
||||
pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS);
|
||||
}
|
||||
|
||||
static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS);
|
||||
}
|
||||
|
||||
static const struct clk_ops at91sam9n12_usb_ops = {
|
||||
.enable = at91sam9n12_clk_usb_enable,
|
||||
.disable = at91sam9n12_clk_usb_disable,
|
||||
.is_enabled = at91sam9n12_clk_usb_is_enabled,
|
||||
.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
|
||||
.round_rate = at91sam9x5_clk_usb_round_rate,
|
||||
.set_rate = at91sam9x5_clk_usb_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name,
|
||||
const char **parent_names, u8 num_parents)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
|
||||
if (!usb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &at91sam9x5_usb_ops;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
|
||||
|
||||
usb->hw.init = &init;
|
||||
usb->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &usb->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(usb);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct clk * __init
|
||||
at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
|
||||
if (!usb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &at91sam9n12_usb_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
usb->hw.init = &init;
|
||||
usb->pmc = pmc;
|
||||
|
||||
clk = clk_register(NULL, &usb->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(usb);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
u32 tmp;
|
||||
u8 usbdiv;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_CKGR_PLLBR);
|
||||
usbdiv = (tmp & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
|
||||
if (usb->divisors[usbdiv])
|
||||
return parent_rate / usb->divisors[usbdiv];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
|
||||
unsigned long bestrate = 0;
|
||||
int bestdiff = -1;
|
||||
unsigned long tmprate;
|
||||
int tmpdiff;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!usb->divisors[i])
|
||||
continue;
|
||||
tmprate = *parent_rate / usb->divisors[i];
|
||||
if (tmprate < rate)
|
||||
tmpdiff = rate - tmprate;
|
||||
else
|
||||
tmpdiff = tmprate - rate;
|
||||
|
||||
if (bestdiff < 0 || bestdiff > tmpdiff) {
|
||||
bestrate = tmprate;
|
||||
bestdiff = tmpdiff;
|
||||
}
|
||||
|
||||
if (!bestdiff)
|
||||
break;
|
||||
}
|
||||
|
||||
return bestrate;
|
||||
}
|
||||
|
||||
static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
int i;
|
||||
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
|
||||
struct at91_pmc *pmc = usb->pmc;
|
||||
unsigned long div = parent_rate / rate;
|
||||
|
||||
if (parent_rate % rate)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
|
||||
if (usb->divisors[i] == div) {
|
||||
tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
|
||||
~AT91_PMC_USBDIV;
|
||||
tmp |= i << RM9200_USB_DIV_SHIFT;
|
||||
pmc_write(pmc, AT91_CKGR_PLLBR, tmp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct clk_ops at91rm9200_usb_ops = {
|
||||
.recalc_rate = at91rm9200_clk_usb_recalc_rate,
|
||||
.round_rate = at91rm9200_clk_usb_round_rate,
|
||||
.set_rate = at91rm9200_clk_usb_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name, const u32 *divisors)
|
||||
{
|
||||
struct at91rm9200_clk_usb *usb;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
|
||||
if (!usb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &at91rm9200_usb_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = 0;
|
||||
|
||||
usb->hw.init = &init;
|
||||
usb->pmc = pmc;
|
||||
memcpy(usb->divisors, divisors, sizeof(usb->divisors));
|
||||
|
||||
clk = clk_register(NULL, &usb->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(usb);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
int num_parents;
|
||||
const char *parent_names[USB_SOURCE_MAX];
|
||||
const char *name = np->name;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (num_parents <= 0 || num_parents > USB_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_parents; i++) {
|
||||
parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
if (!parent_names[i])
|
||||
return;
|
||||
}
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
clk = at91sam9x5_clk_register_usb(pmc, name, parent_names, num_parents);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
||||
void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
clk = at91sam9n12_clk_register_usb(pmc, name, parent_name);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
||||
void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
u32 divisors[4] = {0, 0, 0, 0};
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
|
||||
if (!divisors[0])
|
||||
return;
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
clk = at91rm9200_clk_register_usb(pmc, name, parent_name, divisors);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define UTMI_FIXED_MUL 40
|
||||
|
||||
struct clk_utmi {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
};
|
||||
|
||||
#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
|
||||
|
||||
static irqreturn_t clk_utmi_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_utmi *utmi = (struct clk_utmi *)dev_id;
|
||||
|
||||
wake_up(&utmi->wait);
|
||||
disable_irq_nosync(utmi->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_utmi_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_utmi *utmi = to_clk_utmi(hw);
|
||||
struct at91_pmc *pmc = utmi->pmc;
|
||||
u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN |
|
||||
AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN;
|
||||
|
||||
pmc_write(pmc, AT91_CKGR_UCKR, tmp);
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU)) {
|
||||
enable_irq(utmi->irq);
|
||||
wait_event(utmi->wait,
|
||||
pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_utmi_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_utmi *utmi = to_clk_utmi(hw);
|
||||
struct at91_pmc *pmc = utmi->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU);
|
||||
}
|
||||
|
||||
static void clk_utmi_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_utmi *utmi = to_clk_utmi(hw);
|
||||
struct at91_pmc *pmc = utmi->pmc;
|
||||
u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN;
|
||||
|
||||
pmc_write(pmc, AT91_CKGR_UCKR, tmp);
|
||||
}
|
||||
|
||||
static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/* UTMI clk is a fixed clk multiplier */
|
||||
return parent_rate * UTMI_FIXED_MUL;
|
||||
}
|
||||
|
||||
static const struct clk_ops utmi_ops = {
|
||||
.prepare = clk_utmi_prepare,
|
||||
.unprepare = clk_utmi_unprepare,
|
||||
.is_prepared = clk_utmi_is_prepared,
|
||||
.recalc_rate = clk_utmi_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_utmi(struct at91_pmc *pmc, unsigned int irq,
|
||||
const char *name, const char *parent_name)
|
||||
{
|
||||
int ret;
|
||||
struct clk_utmi *utmi;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
|
||||
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
|
||||
if (!utmi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &utmi_ops;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
utmi->hw.init = &init;
|
||||
utmi->pmc = pmc;
|
||||
utmi->irq = irq;
|
||||
init_waitqueue_head(&utmi->wait);
|
||||
irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN);
|
||||
ret = request_irq(utmi->irq, clk_utmi_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, "clk-utmi", utmi);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &utmi->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(utmi);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91_clk_utmi_setup(struct device_node *np, struct at91_pmc *pmc)
|
||||
{
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
clk = at91_clk_register_utmi(pmc, irq, name, parent_name);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
return;
|
||||
}
|
||||
|
||||
void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc)
|
||||
{
|
||||
of_at91_clk_utmi_setup(np, pmc);
|
||||
}
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
void __iomem *at91_pmc_base;
|
||||
EXPORT_SYMBOL_GPL(at91_pmc_base);
|
||||
|
||||
void at91sam9_idle(void)
|
||||
{
|
||||
at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK);
|
||||
cpu_do_idle();
|
||||
}
|
||||
|
||||
int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
||||
struct clk_range *range)
|
||||
{
|
||||
u32 min, max;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32_index(np, propname, 0, &min);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32_index(np, propname, 1, &max);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (range) {
|
||||
range->min = min;
|
||||
range->max = max;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
|
||||
|
||||
static void pmc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq);
|
||||
}
|
||||
|
||||
static void pmc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq);
|
||||
}
|
||||
|
||||
static int pmc_irq_set_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
if (type != IRQ_TYPE_LEVEL_HIGH) {
|
||||
pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip pmc_irq = {
|
||||
.name = "PMC",
|
||||
.irq_disable = pmc_irq_mask,
|
||||
.irq_mask = pmc_irq_mask,
|
||||
.irq_unmask = pmc_irq_unmask,
|
||||
.irq_set_type = pmc_irq_set_type,
|
||||
};
|
||||
|
||||
static struct lock_class_key pmc_lock_class;
|
||||
|
||||
static int pmc_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct at91_pmc *pmc = h->host_data;
|
||||
|
||||
irq_set_lockdep_class(virq, &pmc_lock_class);
|
||||
|
||||
irq_set_chip_and_handler(virq, &pmc_irq,
|
||||
handle_level_irq);
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
irq_set_chip_data(virq, pmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
struct at91_pmc *pmc = d->host_data;
|
||||
const struct at91_pmc_caps *caps = pmc->caps;
|
||||
|
||||
if (WARN_ON(intsize < 1))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = intspec[0];
|
||||
|
||||
if (!(caps->available_irqs & (1 << *out_hwirq)))
|
||||
return -EINVAL;
|
||||
|
||||
*out_type = IRQ_TYPE_LEVEL_HIGH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops pmc_irq_ops = {
|
||||
.map = pmc_irq_map,
|
||||
.xlate = pmc_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static irqreturn_t pmc_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct at91_pmc *pmc = (struct at91_pmc *)data;
|
||||
unsigned long sr;
|
||||
int n;
|
||||
|
||||
sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR);
|
||||
if (!sr)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(n, &sr, BITS_PER_LONG)
|
||||
generic_handle_irq(irq_find_mapping(pmc->irqdomain, n));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct at91_pmc_caps at91rm9200_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
|
||||
AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
|
||||
AT91_PMC_PCK3RDY,
|
||||
};
|
||||
|
||||
static const struct at91_pmc_caps at91sam9260_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
|
||||
AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY,
|
||||
};
|
||||
|
||||
static const struct at91_pmc_caps at91sam9g45_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
|
||||
AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY,
|
||||
};
|
||||
|
||||
static const struct at91_pmc_caps at91sam9n12_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
|
||||
AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
|
||||
AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
|
||||
};
|
||||
|
||||
static const struct at91_pmc_caps at91sam9x5_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
|
||||
AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
|
||||
AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
|
||||
};
|
||||
|
||||
static const struct at91_pmc_caps sama5d3_caps = {
|
||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
|
||||
AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
|
||||
AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
|
||||
AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS |
|
||||
AT91_PMC_CFDEV,
|
||||
};
|
||||
|
||||
static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
|
||||
void __iomem *regbase, int virq,
|
||||
const struct at91_pmc_caps *caps)
|
||||
{
|
||||
struct at91_pmc *pmc;
|
||||
|
||||
if (!regbase || !virq || !caps)
|
||||
return NULL;
|
||||
|
||||
at91_pmc_base = regbase;
|
||||
|
||||
pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
|
||||
if (!pmc)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&pmc->lock);
|
||||
pmc->regbase = regbase;
|
||||
pmc->virq = virq;
|
||||
pmc->caps = caps;
|
||||
|
||||
pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
|
||||
|
||||
if (!pmc->irqdomain)
|
||||
goto out_free_pmc;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_IDR, 0xffffffff);
|
||||
if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc))
|
||||
goto out_remove_irqdomain;
|
||||
|
||||
return pmc;
|
||||
|
||||
out_remove_irqdomain:
|
||||
irq_domain_remove(pmc->irqdomain);
|
||||
out_free_pmc:
|
||||
kfree(pmc);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct of_device_id pmc_clk_ids[] __initdata = {
|
||||
/* Main clock */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-main",
|
||||
.data = of_at91rm9200_clk_main_setup,
|
||||
},
|
||||
/* PLL clocks */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-pll",
|
||||
.data = of_at91rm9200_clk_pll_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9g45-clk-pll",
|
||||
.data = of_at91sam9g45_clk_pll_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9g20-clk-pllb",
|
||||
.data = of_at91sam9g20_clk_pllb_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,sama5d3-clk-pll",
|
||||
.data = of_sama5d3_clk_pll_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-plldiv",
|
||||
.data = of_at91sam9x5_clk_plldiv_setup,
|
||||
},
|
||||
/* Master clock */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-master",
|
||||
.data = of_at91rm9200_clk_master_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-master",
|
||||
.data = of_at91sam9x5_clk_master_setup,
|
||||
},
|
||||
/* System clocks */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-system",
|
||||
.data = of_at91rm9200_clk_sys_setup,
|
||||
},
|
||||
/* Peripheral clocks */
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-peripheral",
|
||||
.data = of_at91rm9200_clk_periph_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-peripheral",
|
||||
.data = of_at91sam9x5_clk_periph_setup,
|
||||
},
|
||||
/* Programmable clocks */
|
||||
#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS)
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-programmable",
|
||||
.data = of_at91rm9200_clk_prog_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9g45-clk-programmable",
|
||||
.data = of_at91sam9g45_clk_prog_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-programmable",
|
||||
.data = of_at91sam9x5_clk_prog_setup,
|
||||
},
|
||||
#endif
|
||||
/* UTMI clock */
|
||||
#if defined(CONFIG_HAVE_AT91_UTMI)
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-utmi",
|
||||
.data = of_at91sam9x5_clk_utmi_setup,
|
||||
},
|
||||
#endif
|
||||
/* USB clock */
|
||||
#if defined(CONFIG_HAVE_AT91_USB_CLK)
|
||||
{
|
||||
.compatible = "atmel,at91rm9200-clk-usb",
|
||||
.data = of_at91rm9200_clk_usb_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-usb",
|
||||
.data = of_at91sam9x5_clk_usb_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9n12-clk-usb",
|
||||
.data = of_at91sam9n12_clk_usb_setup,
|
||||
},
|
||||
#endif
|
||||
/* SMD clock */
|
||||
#if defined(CONFIG_HAVE_AT91_SMD)
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-smd",
|
||||
.data = of_at91sam9x5_clk_smd_setup,
|
||||
},
|
||||
#endif
|
||||
{ /*sentinel*/ }
|
||||
};
|
||||
|
||||
static void __init of_at91_pmc_setup(struct device_node *np,
|
||||
const struct at91_pmc_caps *caps)
|
||||
{
|
||||
struct at91_pmc *pmc;
|
||||
struct device_node *childnp;
|
||||
void (*clk_setup)(struct device_node *, struct at91_pmc *);
|
||||
const struct of_device_id *clk_id;
|
||||
void __iomem *regbase = of_iomap(np, 0);
|
||||
int virq;
|
||||
|
||||
if (!regbase)
|
||||
return;
|
||||
|
||||
virq = irq_of_parse_and_map(np, 0);
|
||||
if (!virq)
|
||||
return;
|
||||
|
||||
pmc = at91_pmc_init(np, regbase, virq, caps);
|
||||
if (!pmc)
|
||||
return;
|
||||
for_each_child_of_node(np, childnp) {
|
||||
clk_id = of_match_node(pmc_clk_ids, childnp);
|
||||
if (!clk_id)
|
||||
continue;
|
||||
clk_setup = clk_id->data;
|
||||
clk_setup(childnp, pmc);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init of_at91rm9200_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &at91rm9200_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(at91rm9200_clk_pmc, "atmel,at91rm9200-pmc",
|
||||
of_at91rm9200_pmc_setup);
|
||||
|
||||
static void __init of_at91sam9260_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &at91sam9260_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(at91sam9260_clk_pmc, "atmel,at91sam9260-pmc",
|
||||
of_at91sam9260_pmc_setup);
|
||||
|
||||
static void __init of_at91sam9g45_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &at91sam9g45_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(at91sam9g45_clk_pmc, "atmel,at91sam9g45-pmc",
|
||||
of_at91sam9g45_pmc_setup);
|
||||
|
||||
static void __init of_at91sam9n12_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &at91sam9n12_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(at91sam9n12_clk_pmc, "atmel,at91sam9n12-pmc",
|
||||
of_at91sam9n12_pmc_setup);
|
||||
|
||||
static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &at91sam9x5_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc",
|
||||
of_at91sam9x5_pmc_setup);
|
||||
|
||||
static void __init of_sama5d3_pmc_setup(struct device_node *np)
|
||||
{
|
||||
of_at91_pmc_setup(np, &sama5d3_caps);
|
||||
}
|
||||
CLK_OF_DECLARE(sama5d3_clk_pmc, "atmel,sama5d3-pmc",
|
||||
of_sama5d3_pmc_setup);
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* drivers/clk/at91/pmc.h
|
||||
*
|
||||
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.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 __PMC_H_
|
||||
#define __PMC_H_
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct clk_range {
|
||||
unsigned long min;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
|
||||
|
||||
struct at91_pmc_caps {
|
||||
u32 available_irqs;
|
||||
};
|
||||
|
||||
struct at91_pmc {
|
||||
void __iomem *regbase;
|
||||
int virq;
|
||||
spinlock_t lock;
|
||||
const struct at91_pmc_caps *caps;
|
||||
struct irq_domain *irqdomain;
|
||||
};
|
||||
|
||||
static inline void pmc_lock(struct at91_pmc *pmc)
|
||||
{
|
||||
spin_lock(&pmc->lock);
|
||||
}
|
||||
|
||||
static inline void pmc_unlock(struct at91_pmc *pmc)
|
||||
{
|
||||
spin_unlock(&pmc->lock);
|
||||
}
|
||||
|
||||
static inline u32 pmc_read(struct at91_pmc *pmc, int offset)
|
||||
{
|
||||
return readl(pmc->regbase + offset);
|
||||
}
|
||||
|
||||
static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value)
|
||||
{
|
||||
writel(value, pmc->regbase + offset);
|
||||
}
|
||||
|
||||
int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
||||
struct clk_range *range);
|
||||
|
||||
extern void __init of_at91rm9200_clk_main_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
|
||||
extern void __init of_at91rm9200_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9g45_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_sama5d3_clk_pll_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
|
||||
extern void __init of_at91rm9200_clk_master_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
|
||||
extern void __init of_at91rm9200_clk_sys_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
|
||||
extern void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
|
||||
#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS)
|
||||
extern void __init of_at91rm9200_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9g45_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9x5_clk_prog_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HAVE_AT91_UTMI)
|
||||
extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HAVE_AT91_USB_CLK)
|
||||
extern void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HAVE_AT91_SMD)
|
||||
extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
|
||||
struct at91_pmc *pmc);
|
||||
#endif
|
||||
|
||||
#endif /* __PMC_H_ */
|
|
@ -326,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
|
|||
|
||||
#if defined(CONFIG_ARCH_AT91SAM9RL)
|
||||
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
||||
static void toggle_bias(int is_on)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* This header provides constants for AT91 pmc status.
|
||||
*
|
||||
* The constants defined in this header are being used in dts.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_CLK_AT91_H
|
||||
#define _DT_BINDINGS_CLK_AT91_H
|
||||
|
||||
#define AT91_PMC_MOSCS 0 /* MOSCS Flag */
|
||||
#define AT91_PMC_LOCKA 1 /* PLLA Lock */
|
||||
#define AT91_PMC_LOCKB 2 /* PLLB Lock */
|
||||
#define AT91_PMC_MCKRDY 3 /* Master Clock */
|
||||
#define AT91_PMC_LOCKU 6 /* UPLL Lock */
|
||||
#define AT91_PMC_PCKRDY(id) (8 + (id)) /* Programmable Clock */
|
||||
#define AT91_PMC_MOSCSELS 16 /* Main Oscillator Selection */
|
||||
#define AT91_PMC_MOSCRCS 17 /* Main On-Chip RC */
|
||||
#define AT91_PMC_CFDEV 18 /* Clock Failure Detector Event */
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* arch/arm/mach-at91/include/mach/at91_pmc.h
|
||||
* include/linux/clk/at91_pmc.h
|
||||
*
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
* Copyright (C) SAN People
|
||||
|
@ -164,6 +164,8 @@ extern void __iomem *at91_pmc_base;
|
|||
#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
|
||||
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
|
||||
|
||||
#define AT91_PMC_PLLICPR 0x80 /* PLL Charge Pump Current Register */
|
||||
|
||||
#define AT91_PMC_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */
|
||||
#define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */
|
||||
#define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */
|
Loading…
Reference in New Issue