Merge branch 'at91/dt' into next/drivers

Merging at91/dt as a prereq for the at91/drivers code.

* at91/dt: (43 commits)
  ARM: at91/at91rm9200ek.dts: rearrange nodes in address ascending order
  ARM: at91: dt: at91rm9200ek: add emac and nor flash support
  ARM: at91: add uart aliases to sama5d3 dtsi
  ARM: at91: add i2c2 pinctrl speficifation to sama5d3 DT
  ARM: at91: Animeo IP: fix mtd partition table
  ARM: at91: at91sam9g45: add i2c pinctrl
  ARM: at91: at91sam9g45: set default mmc pinctrl-names
  ARM: at91: sama5d3: enable qt1070 as a wakeup source
  ARM: at91: add support for Cosino board series by HCE Engineering
  ARM: at91/dt/sama5d3: add DMA information to SHA/AES/TDES nodes
  ARM: at91/dt/trivial: before sama5d3, Atmel MPU were using at91 prefix
  ARM: at91/dt/trivial: use macro for AES irq type
  ARM: at91: sam9263ek: add dt lcd support
  ARM: at91: at9sam9m10g45ek: add dt lcd support
  ARM: at91: sam9263: add fb dt support
  ARM: at91: sam9g45: add fb dt support
  ARM: at91/dt: binding: add missing compatibility string in SDRAM/DDR documentation
  ARM: at91/dt: binding: add precision to AIC documentation
  ARM: at91/dt: add atmel,pullup-gpio to at91rm9200ek usb1 definition
  ARM: at91/dt: add ethernet phy to at91rm9200ek board
  ...
This commit is contained in:
Olof Johansson 2013-12-22 11:42:30 -08:00
commit 958c025a2a
59 changed files with 4694 additions and 416 deletions

View File

@ -2,6 +2,7 @@
Required properties: Required properties:
- compatible: Should be "atmel,<chip>-aic" - compatible: Should be "atmel,<chip>-aic"
<chip> can be "at91rm9200" or "sama5d3"
- interrupt-controller: Identifies the node as an interrupt controller. - interrupt-controller: Identifies the node as an interrupt controller.
- interrupt-parent: For single AIC system, it is an empty property. - interrupt-parent: For single AIC system, it is an empty property.
- #interrupt-cells: The number of cells to define the interrupts. It should be 3. - #interrupt-cells: The number of cells to define the interrupts. It should be 3.

View File

@ -50,7 +50,8 @@ Example:
}; };
RAMC SDRAM/DDR Controller required properties: RAMC SDRAM/DDR Controller required properties:
- compatible: Should be "atmel,at91sam9260-sdramc", - compatible: Should be "atmel,at91rm9200-sdramc",
"atmel,at91sam9260-sdramc",
"atmel,at91sam9g45-ddramc", "atmel,at91sam9g45-ddramc",
- reg: Should contain registers location and length - reg: Should contain registers location and length
For at91sam9263 and at91sam9g45 you must specify 2 entries. For at91sam9263 and at91sam9g45 you must specify 2 entries.

View File

@ -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>;
};

View File

@ -30,6 +30,7 @@ dtb-$(CONFIG_ARCH_AT91) += pm9g45.dtb
dtb-$(CONFIG_ARCH_AT91) += at91sam9n12ek.dtb dtb-$(CONFIG_ARCH_AT91) += at91sam9n12ek.dtb
# sam9x5 # sam9x5
dtb-$(CONFIG_ARCH_AT91) += at91-ariag25.dtb dtb-$(CONFIG_ARCH_AT91) += at91-ariag25.dtb
dtb-$(CONFIG_ARCH_AT91) += at91-cosino_mega2560.dtb
dtb-$(CONFIG_ARCH_AT91) += at91sam9g15ek.dtb dtb-$(CONFIG_ARCH_AT91) += at91sam9g15ek.dtb
dtb-$(CONFIG_ARCH_AT91) += at91sam9g25ek.dtb dtb-$(CONFIG_ARCH_AT91) += at91sam9g25ek.dtb
dtb-$(CONFIG_ARCH_AT91) += at91sam9g35ek.dtb dtb-$(CONFIG_ARCH_AT91) += at91sam9g35ek.dtb

View File

@ -90,34 +90,19 @@
nand-on-flash-bbt; nand-on-flash-bbt;
status = "okay"; status = "okay";
at91bootstrap@0 { barebox@0 {
label = "at91bootstrap";
reg = <0x0 0x8000>;
};
barebox@8000 {
label = "barebox"; label = "barebox";
reg = <0x8000 0x40000>; reg = <0x0 0x58000>;
}; };
bareboxenv@48000 { u_boot_env@58000 {
label = "bareboxenv"; label = "u_boot_env";
reg = <0x48000 0x8000>; reg = <0x58000 0x8000>;
}; };
user_block@0x50000 { ubi@60000 {
label = "user_block"; label = "ubi";
reg = <0x50000 0xb0000>; reg = <0x60000 0x1FA0000>;
};
kernel@100000 {
label = "kernel";
reg = <0x100000 0x1b0000>;
};
root@2b0000 {
label = "root";
reg = <0x2b0000 0x1D50000>;
}; };
}; };

View File

@ -0,0 +1,122 @@
/*
* at91-cosino.dtsi - Device Tree file for Cosino core module
*
* Copyright (C) 2013 - Rodolfo Giometti <giometti@linux.it>
* HCE Engineering
*
* Derived from at91sam9x5ek.dtsi by:
* Copyright (C) 2012 Atmel,
* 2012 Nicolas Ferre <nicolas.ferre@atmel.com>
*
* Licensed under GPLv2 or later.
*/
#include "at91sam9g35.dtsi"
/ {
model = "HCE Cosino core module";
compatible = "hce,cosino", "atmel,at91sam9x5", "atmel,at91sam9";
chosen {
bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext3 rootwait";
};
memory {
reg = <0x20000000 0x8000000>;
};
clocks {
#address-cells = <1>;
#size-cells = <1>;
ranges;
main_clock: clock@0 {
compatible = "atmel,osc", "fixed-clock";
clock-frequency = <12000000>;
};
};
ahb {
apb {
mmc0: mmc@f0008000 {
pinctrl-0 = <
&pinctrl_board_mmc0
&pinctrl_mmc0_slot0_clk_cmd_dat0
&pinctrl_mmc0_slot0_dat1_3>;
status = "okay";
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 GPIO_ACTIVE_HIGH>;
};
};
dbgu: serial@fffff200 {
status = "okay";
};
usart0: serial@f801c000 {
status = "okay";
};
i2c0: i2c@f8010000 {
status = "okay";
};
adc0: adc@f804c000 {
atmel,adc-clock-rate = <1000000>;
atmel,adc-ts-wires = <4>;
atmel,adc-ts-pressure-threshold = <10000>;
status = "okay";
};
pinctrl@fffff400 {
mmc0 {
pinctrl_board_mmc0: mmc0-board {
atmel,pins =
<AT91_PIOD 15 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP_DEGLITCH>; /* PD15 gpio CD pin pull up and deglitch */
};
};
};
watchdog@fffffe40 {
status = "okay";
};
};
nand0: nand@40000000 {
nand-bus-width = <8>;
nand-ecc-mode = "hw";
atmel,has-pmecc; /* Enable PMECC */
atmel,pmecc-cap = <4>;
atmel,pmecc-sector-size = <512>;
nand-on-flash-bbt;
status = "okay";
at91bootstrap@0 {
label = "at91bootstrap";
reg = <0x0 0x40000>;
};
uboot@40000 {
label = "u-boot";
reg = <0x40000 0x80000>;
};
ubootenv@c0000 {
label = "U-Boot Env";
reg = <0xc0000 0x140000>;
};
kernel@200000 {
label = "kernel";
reg = <0x200000 0x600000>;
};
rootfs@800000 {
label = "rootfs";
reg = <0x800000 0x0f800000>;
};
};
};
};

View File

@ -0,0 +1,84 @@
/*
* at91-cosino_mega2560.dts - Device Tree file for Cosino board with
* Mega 2560 extension
*
* Copyright (C) 2013 - Rodolfo Giometti <giometti@linux.it>
* HCE Engineering
*
* Derived from at91sam9g35ek.dts by:
* Copyright (C) 2012 Atmel,
* 2012 Nicolas Ferre <nicolas.ferre@atmel.com>
*
* Licensed under GPLv2 or later.
*/
/dts-v1/;
#include "at91-cosino.dtsi"
/ {
model = "HCE Cosino Mega 2560";
compatible = "hce,cosino_mega2560", "atmel,at91sam9x5", "atmel,at91sam9";
ahb {
apb {
macb0: ethernet@f802c000 {
phy-mode = "rmii";
status = "okay";
};
adc0: adc@f804c000 {
atmel,adc-clock-rate = <1000000>;
atmel,adc-ts-wires = <4>;
atmel,adc-ts-pressure-threshold = <10000>;
status = "okay";
};
tsadcc: tsadcc@f804c000 {
status = "okay";
};
rtc@fffffeb0 {
status = "okay";
};
usart1: serial@f8020000 {
status = "okay";
};
usart2: serial@f8024000 {
status = "okay";
};
usb2: gadget@f803c000 {
atmel,vbus-gpio = <&pioB 16 GPIO_ACTIVE_HIGH>;
status = "okay";
};
mmc1: mmc@f000c000 {
pinctrl-0 = <
&pinctrl_mmc1_slot0_clk_cmd_dat0
&pinctrl_mmc1_slot0_dat1_3>;
status = "okay";
slot@0 {
reg = <0>;
bus-width = <4>;
non-removable;
};
};
};
usb0: ohci@00600000 {
status = "okay";
num-ports = <3>;
atmel,vbus-gpio = <0 /* &pioD 18 GPIO_ACTIVE_LOW */
&pioD 19 GPIO_ACTIVE_LOW
&pioD 20 GPIO_ACTIVE_LOW
>;
};
usb1: ehci@00700000 {
status = "okay";
};
};
};

View File

@ -191,12 +191,12 @@
AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA18 periph A */ AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA18 periph A */
}; };
pinctrl_uart0_rts: uart0_rts-0 { pinctrl_uart0_cts: uart0_cts-0 {
atmel,pins = atmel,pins =
<AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA20 periph A */ <AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA20 periph A */
}; };
pinctrl_uart0_cts: uart0_cts-0 { pinctrl_uart0_rts: uart0_rts-0 {
atmel,pins = atmel,pins =
<AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA21 periph A */ <AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA21 periph A */
}; };

View File

@ -29,10 +29,22 @@
ahb { ahb {
apb { apb {
dbgu: serial@fffff200 { usb1: gadget@fffb0000 {
atmel,vbus-gpio = <&pioD 4 GPIO_ACTIVE_HIGH>;
atmel,pullup-gpio = <&pioD 5 GPIO_ACTIVE_HIGH>;
status = "okay"; status = "okay";
}; };
macb0: ethernet@fffbc000 {
phy-mode = "rmii";
status = "okay";
phy0: ethernet-phy {
interrupt-parent = <&pioC>;
interrupts = <4 IRQ_TYPE_EDGE_BOTH>;
};
};
usart1: serial@fffc4000 { usart1: serial@fffc4000 {
pinctrl-0 = pinctrl-0 =
<&pinctrl_uart1 <&pinctrl_uart1
@ -44,16 +56,6 @@
status = "okay"; status = "okay";
}; };
macb0: ethernet@fffbc000 {
phy-mode = "rmii";
status = "okay";
};
usb1: gadget@fffb0000 {
atmel,vbus-gpio = <&pioD 4 GPIO_ACTIVE_HIGH>;
status = "okay";
};
spi0: spi@fffe0000 { spi0: spi@fffe0000 {
status = "okay"; status = "okay";
cs-gpios = <&pioA 3 0>, <0>, <0>, <0>; cs-gpios = <&pioA 3 0>, <0>, <0>, <0>;
@ -63,12 +65,45 @@
reg = <0>; reg = <0>;
}; };
}; };
dbgu: serial@fffff200 {
status = "okay";
};
}; };
usb0: ohci@00300000 { usb0: ohci@00300000 {
num-ports = <2>; num-ports = <2>;
status = "okay"; status = "okay";
}; };
nor_flash@10000000 {
compatible = "cfi-flash";
reg = <0x10000000 0x800000>;
linux,mtd-name = "physmap-flash.0";
bank-width = <2>;
#address-cells = <1>;
#size-cells = <1>;
barebox@0 {
label = "barebox";
reg = <0x00000 0x40000>;
};
bareboxenv@40000 {
label = "bareboxenv";
reg = <0x40000 0x10000>;
};
kernel@50000 {
label = "kernel";
reg = <0x50000 0x300000>;
};
root@350000 {
label = "root";
reg = <0x350000 0x4B0000>;
};
};
}; };
leds { leds {

View File

@ -366,6 +366,34 @@
}; };
}; };
fb {
pinctrl_fb: fb-0 {
atmel,pins =
<AT91_PIOC 1 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC1 periph A */
AT91_PIOC 2 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC2 periph A */
AT91_PIOC 3 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC3 periph A */
AT91_PIOB 9 AT91_PERIPH_B AT91_PINCTRL_NONE /* PB9 periph B */
AT91_PIOC 6 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC6 periph A */
AT91_PIOC 7 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC7 periph A */
AT91_PIOC 8 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC8 periph A */
AT91_PIOC 9 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC9 periph A */
AT91_PIOC 10 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC10 periph A */
AT91_PIOC 11 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC11 periph A */
AT91_PIOC 14 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC14 periph A */
AT91_PIOC 15 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC15 periph A */
AT91_PIOC 16 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC16 periph A */
AT91_PIOC 12 AT91_PERIPH_B AT91_PINCTRL_NONE /* PC12 periph B */
AT91_PIOC 18 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC18 periph A */
AT91_PIOC 19 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC19 periph A */
AT91_PIOC 22 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC22 periph A */
AT91_PIOC 23 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC23 periph A */
AT91_PIOC 24 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC24 periph A */
AT91_PIOC 17 AT91_PERIPH_B AT91_PINCTRL_NONE /* PC17 periph B */
AT91_PIOC 26 AT91_PERIPH_A AT91_PINCTRL_NONE /* PC26 periph A */
AT91_PIOC 27 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PC27 periph A */
};
};
pioA: gpio@fffff200 { pioA: gpio@fffff200 {
compatible = "atmel,at91rm9200-gpio"; compatible = "atmel,at91rm9200-gpio";
reg = <0xfffff200 0x200>; reg = <0xfffff200 0x200>;
@ -549,6 +577,15 @@
}; };
}; };
fb0: fb@0x00700000 {
compatible = "atmel,at91sam9263-lcdc";
reg = <0x00700000 0x1000>;
interrupts = <26 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fb>;
status = "disabled";
};
nand0: nand@40000000 { nand0: nand@40000000 {
compatible = "atmel,at91rm9200-nand"; compatible = "atmel,at91rm9200-nand";
#address-cells = <1>; #address-cells = <1>;

View File

@ -95,6 +95,36 @@
}; };
}; };
fb0: fb@0x00700000 {
display = <&display0>;
status = "okay";
display0: display {
bits-per-pixel = <16>;
atmel,lcdcon-backlight;
atmel,dmacon = <0x1>;
atmel,lcdcon2 = <0x80008002>;
atmel,guard-time = <1>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <4965000>;
hactive = <240>;
vactive = <320>;
hback-porch = <1>;
hfront-porch = <33>;
vback-porch = <1>;
vfront-porch = <0>;
hsync-len = <5>;
vsync-len = <1>;
hsync-active = <1>;
vsync-active = <1>;
};
};
};
};
nand0: nand@40000000 { nand0: nand@40000000 {
nand-bus-width = <8>; nand-bus-width = <8>;
nand-ecc-mode = "soft"; nand-ecc-mode = "soft";

View File

@ -143,6 +143,22 @@
}; };
}; };
i2c0 {
pinctrl_i2c0: i2c0-0 {
atmel,pins =
<AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE /* PA21 periph A TWCK0 */
AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PA20 periph A TWD0 */
};
};
i2c1 {
pinctrl_i2c1: i2c1-0 {
atmel,pins =
<AT91_PIOB 11 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB11 periph A TWCK1 */
AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PB10 periph A TWD1 */
};
};
usart0 { usart0 {
pinctrl_usart0: usart0-0 { pinctrl_usart0: usart0-0 {
atmel,pins = atmel,pins =
@ -425,6 +441,42 @@
}; };
}; };
fb {
pinctrl_fb: fb-0 {
atmel,pins =
<AT91_PIOE 0 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE0 periph A */
AT91_PIOE 2 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE2 periph A */
AT91_PIOE 3 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE3 periph A */
AT91_PIOE 4 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE4 periph A */
AT91_PIOE 5 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE5 periph A */
AT91_PIOE 6 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE6 periph A */
AT91_PIOE 7 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE7 periph A */
AT91_PIOE 8 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE8 periph A */
AT91_PIOE 9 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE9 periph A */
AT91_PIOE 10 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE10 periph A */
AT91_PIOE 11 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE11 periph A */
AT91_PIOE 12 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE12 periph A */
AT91_PIOE 13 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE13 periph A */
AT91_PIOE 14 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE14 periph A */
AT91_PIOE 15 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE15 periph A */
AT91_PIOE 16 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE16 periph A */
AT91_PIOE 17 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE17 periph A */
AT91_PIOE 18 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE18 periph A */
AT91_PIOE 19 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE19 periph A */
AT91_PIOE 20 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE20 periph A */
AT91_PIOE 21 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE21 periph A */
AT91_PIOE 22 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE22 periph A */
AT91_PIOE 23 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE23 periph A */
AT91_PIOE 24 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE24 periph A */
AT91_PIOE 25 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE25 periph A */
AT91_PIOE 26 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE26 periph A */
AT91_PIOE 27 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE27 periph A */
AT91_PIOE 28 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE28 periph A */
AT91_PIOE 29 AT91_PERIPH_A AT91_PINCTRL_NONE /* PE29 periph A */
AT91_PIOE 30 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PE30 periph A */
};
};
pioA: gpio@fffff200 { pioA: gpio@fffff200 {
compatible = "atmel,at91rm9200-gpio"; compatible = "atmel,at91rm9200-gpio";
reg = <0xfffff200 0x200>; reg = <0xfffff200 0x200>;
@ -542,6 +594,8 @@
compatible = "atmel,at91sam9g10-i2c"; compatible = "atmel,at91sam9g10-i2c";
reg = <0xfff84000 0x100>; reg = <0xfff84000 0x100>;
interrupts = <12 IRQ_TYPE_LEVEL_HIGH 6>; interrupts = <12 IRQ_TYPE_LEVEL_HIGH 6>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c0>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
status = "disabled"; status = "disabled";
@ -551,6 +605,8 @@
compatible = "atmel,at91sam9g10-i2c"; compatible = "atmel,at91sam9g10-i2c";
reg = <0xfff88000 0x100>; reg = <0xfff88000 0x100>;
interrupts = <13 IRQ_TYPE_LEVEL_HIGH 6>; interrupts = <13 IRQ_TYPE_LEVEL_HIGH 6>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
status = "disabled"; status = "disabled";
@ -618,6 +674,7 @@
compatible = "atmel,hsmci"; compatible = "atmel,hsmci";
reg = <0xfff80000 0x600>; reg = <0xfff80000 0x600>;
interrupts = <11 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <11 IRQ_TYPE_LEVEL_HIGH 0>;
pinctrl-names = "default";
dmas = <&dma 1 AT91_DMA_CFG_PER_ID(0)>; dmas = <&dma 1 AT91_DMA_CFG_PER_ID(0)>;
dma-names = "rxtx"; dma-names = "rxtx";
#address-cells = <1>; #address-cells = <1>;
@ -629,6 +686,7 @@
compatible = "atmel,hsmci"; compatible = "atmel,hsmci";
reg = <0xfffd0000 0x600>; reg = <0xfffd0000 0x600>;
interrupts = <29 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <29 IRQ_TYPE_LEVEL_HIGH 0>;
pinctrl-names = "default";
dmas = <&dma 1 AT91_DMA_CFG_PER_ID(13)>; dmas = <&dma 1 AT91_DMA_CFG_PER_ID(13)>;
dma-names = "rxtx"; dma-names = "rxtx";
#address-cells = <1>; #address-cells = <1>;
@ -727,6 +785,15 @@
}; };
}; };
fb0: fb@0x00500000 {
compatible = "atmel,at91sam9g45-lcdc";
reg = <0x00500000 0x1000>;
interrupts = <23 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fb>;
status = "disabled";
};
nand0: nand@40000000 { nand0: nand@40000000 {
compatible = "atmel,at91rm9200-nand"; compatible = "atmel,at91rm9200-nand";
#address-cells = <1>; #address-cells = <1>;

View File

@ -123,6 +123,35 @@
}; };
}; };
fb0: fb@0x00500000 {
display = <&display0>;
status = "okay";
display0: display {
bits-per-pixel = <32>;
atmel,lcdcon-backlight;
atmel,dmacon = <0x1>;
atmel,lcdcon2 = <0x80008002>;
atmel,guard-time = <9>;
atmel,lcd-wiring-mode = "RGB";
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <9000000>;
hactive = <480>;
vactive = <272>;
hback-porch = <1>;
hfront-porch = <1>;
vback-porch = <40>;
vfront-porch = <1>;
hsync-len = <45>;
vsync-len = <1>;
};
};
};
};
nand0: nand@40000000 { nand0: nand@40000000 {
nand-bus-width = <8>; nand-bus-width = <8>;
nand-ecc-mode = "soft"; nand-ecc-mode = "soft";

View File

@ -13,6 +13,7 @@
#include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clk/at91.h>
/ { / {
model = "Atmel SAMA5D3 family SoC"; model = "Atmel SAMA5D3 family SoC";
@ -56,6 +57,14 @@
reg = <0x20000000 0x8000000>; reg = <0x20000000 0x8000000>;
}; };
clocks {
adc_op_clk: adc_op_clk{
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <20000000>;
};
};
ahb { ahb {
compatible = "simple-bus"; compatible = "simple-bus";
#address-cells = <1>; #address-cells = <1>;
@ -79,6 +88,8 @@
status = "disabled"; status = "disabled";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
clocks = <&mci0_clk>;
clock-names = "mci_clk";
}; };
spi0: spi@f0004000 { spi0: spi@f0004000 {
@ -92,6 +103,8 @@
dma-names = "tx", "rx"; dma-names = "tx", "rx";
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi0>; pinctrl-0 = <&pinctrl_spi0>;
clocks = <&spi0_clk>;
clock-names = "spi_clk";
status = "disabled"; status = "disabled";
}; };
@ -101,6 +114,8 @@
interrupts = <38 IRQ_TYPE_LEVEL_HIGH 4>; interrupts = <38 IRQ_TYPE_LEVEL_HIGH 4>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>; pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
clocks = <&ssc0_clk>;
clock-names = "pclk";
status = "disabled"; status = "disabled";
}; };
@ -108,6 +123,8 @@
compatible = "atmel,at91sam9x5-tcb"; compatible = "atmel,at91sam9x5-tcb";
reg = <0xf0010000 0x100>; reg = <0xf0010000 0x100>;
interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&tcb0_clk>;
clock-names = "t0_clk";
}; };
i2c0: i2c@f0014000 { i2c0: i2c@f0014000 {
@ -121,6 +138,7 @@
pinctrl-0 = <&pinctrl_i2c0>; pinctrl-0 = <&pinctrl_i2c0>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
clocks = <&twi0_clk>;
status = "disabled"; status = "disabled";
}; };
@ -135,6 +153,7 @@
pinctrl-0 = <&pinctrl_i2c1>; pinctrl-0 = <&pinctrl_i2c1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
clocks = <&twi1_clk>;
status = "disabled"; status = "disabled";
}; };
@ -144,6 +163,8 @@
interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usart0>; pinctrl-0 = <&pinctrl_usart0>;
clocks = <&usart0_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
@ -153,6 +174,8 @@
interrupts = <13 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <13 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usart1>; pinctrl-0 = <&pinctrl_usart1>;
clocks = <&usart1_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
@ -174,6 +197,8 @@
status = "disabled"; status = "disabled";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
clocks = <&mci1_clk>;
clock-names = "mci_clk";
}; };
spi1: spi@f8008000 { spi1: spi@f8008000 {
@ -187,6 +212,8 @@
dma-names = "tx", "rx"; dma-names = "tx", "rx";
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi1>; pinctrl-0 = <&pinctrl_spi1>;
clocks = <&spi1_clk>;
clock-names = "spi_clk";
status = "disabled"; status = "disabled";
}; };
@ -196,6 +223,8 @@
interrupts = <39 IRQ_TYPE_LEVEL_HIGH 4>; interrupts = <39 IRQ_TYPE_LEVEL_HIGH 4>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>; pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
clocks = <&ssc1_clk>;
clock-names = "pclk";
status = "disabled"; status = "disabled";
}; };
@ -219,6 +248,9 @@
&pinctrl_adc0_ad10 &pinctrl_adc0_ad10
&pinctrl_adc0_ad11 &pinctrl_adc0_ad11
>; >;
clocks = <&adc_clk>,
<&adc_op_clk>;
clock-names = "adc_clk", "adc_op_clk";
atmel,adc-channel-base = <0x50>; atmel,adc-channel-base = <0x50>;
atmel,adc-channels-used = <0xfff>; atmel,adc-channels-used = <0xfff>;
atmel,adc-drdy-mask = <0x1000000>; atmel,adc-drdy-mask = <0x1000000>;
@ -272,8 +304,11 @@
dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(11)>, dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(11)>,
<&dma1 2 AT91_DMA_CFG_PER_ID(12)>; <&dma1 2 AT91_DMA_CFG_PER_ID(12)>;
dma-names = "tx", "rx"; dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
clocks = <&twi2_clk>;
status = "disabled"; status = "disabled";
}; };
@ -283,6 +318,8 @@
interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usart2>; pinctrl-0 = <&pinctrl_usart2>;
clocks = <&usart2_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
@ -292,25 +329,35 @@
interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usart3>; pinctrl-0 = <&pinctrl_usart3>;
clocks = <&usart3_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
sha@f8034000 { sha@f8034000 {
compatible = "atmel,sam9g46-sha"; compatible = "atmel,at91sam9g46-sha";
reg = <0xf8034000 0x100>; reg = <0xf8034000 0x100>;
interrupts = <42 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <42 IRQ_TYPE_LEVEL_HIGH 0>;
dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(17)>;
dma-names = "tx";
}; };
aes@f8038000 { aes@f8038000 {
compatible = "atmel,sam9g46-aes"; compatible = "atmel,at91sam9g46-aes";
reg = <0xf8038000 0x100>; reg = <0xf8038000 0x100>;
interrupts = <43 4 0>; interrupts = <43 IRQ_TYPE_LEVEL_HIGH 0>;
dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(18)>,
<&dma1 2 AT91_DMA_CFG_PER_ID(19)>;
dma-names = "tx", "rx";
}; };
tdes@f803c000 { tdes@f803c000 {
compatible = "atmel,sam9g46-tdes"; compatible = "atmel,at91sam9g46-tdes";
reg = <0xf803c000 0x100>; reg = <0xf803c000 0x100>;
interrupts = <44 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <44 IRQ_TYPE_LEVEL_HIGH 0>;
dmas = <&dma1 2 AT91_DMA_CFG_PER_ID(20)>,
<&dma1 2 AT91_DMA_CFG_PER_ID(21)>;
dma-names = "tx", "rx";
}; };
dma0: dma-controller@ffffe600 { dma0: dma-controller@ffffe600 {
@ -318,6 +365,8 @@
reg = <0xffffe600 0x200>; reg = <0xffffe600 0x200>;
interrupts = <30 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <30 IRQ_TYPE_LEVEL_HIGH 0>;
#dma-cells = <2>; #dma-cells = <2>;
clocks = <&dma0_clk>;
clock-names = "dma_clk";
}; };
dma1: dma-controller@ffffe800 { dma1: dma-controller@ffffe800 {
@ -325,6 +374,8 @@
reg = <0xffffe800 0x200>; reg = <0xffffe800 0x200>;
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
#dma-cells = <2>; #dma-cells = <2>;
clocks = <&dma1_clk>;
clock-names = "dma_clk";
}; };
ramc0: ramc@ffffea00 { ramc0: ramc@ffffea00 {
@ -338,6 +389,8 @@
interrupts = <2 IRQ_TYPE_LEVEL_HIGH 7>; interrupts = <2 IRQ_TYPE_LEVEL_HIGH 7>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_dbgu>; pinctrl-0 = <&pinctrl_dbgu>;
clocks = <&dbgu_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
@ -443,6 +496,14 @@
}; };
}; };
i2c2 {
pinctrl_i2c2: i2c2-0 {
atmel,pins =
<AT91_PIOA 18 AT91_PERIPH_B AT91_PINCTRL_NONE /* TWD2 pin, conflicts with LCDDAT18, ISI_D2 */
AT91_PIOA 19 AT91_PERIPH_B AT91_PINCTRL_NONE>; /* TWCK2 pin, conflicts with LCDDAT19, ISI_D3 */
};
};
isi { isi {
pinctrl_isi: isi-0 { pinctrl_isi: isi-0 {
atmel,pins = atmel,pins =
@ -626,6 +687,7 @@
gpio-controller; gpio-controller;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
clocks = <&pioA_clk>;
}; };
pioB: gpio@fffff400 { pioB: gpio@fffff400 {
@ -636,6 +698,7 @@
gpio-controller; gpio-controller;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
clocks = <&pioB_clk>;
}; };
pioC: gpio@fffff600 { pioC: gpio@fffff600 {
@ -646,6 +709,7 @@
gpio-controller; gpio-controller;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
clocks = <&pioC_clk>;
}; };
pioD: gpio@fffff800 { pioD: gpio@fffff800 {
@ -656,6 +720,7 @@
gpio-controller; gpio-controller;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
clocks = <&pioD_clk>;
}; };
pioE: gpio@fffffa00 { pioE: gpio@fffffa00 {
@ -666,12 +731,334 @@
gpio-controller; gpio-controller;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
clocks = <&pioE_clk>;
}; };
}; };
pmc: pmc@fffffc00 { pmc: pmc@fffffc00 {
compatible = "atmel,at91rm9200-pmc"; compatible = "atmel,sama5d3-pmc";
reg = <0xfffffc00 0x120>; 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 { rstc@fffffe00 {
@ -683,6 +1070,7 @@
compatible = "atmel,at91sam9260-pit"; compatible = "atmel,at91sam9260-pit";
reg = <0xfffffe30 0xf>; reg = <0xfffffe30 0xf>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH 5>;
clocks = <&mck>;
}; };
watchdog@fffffe40 { watchdog@fffffe40 {
@ -705,6 +1093,8 @@
reg = <0x00500000 0x100000 reg = <0x00500000 0x100000
0xf8030000 0x4000>; 0xf8030000 0x4000>;
interrupts = <33 IRQ_TYPE_LEVEL_HIGH 2>; interrupts = <33 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&udphs_clk>, <&utmi>;
clock-names = "pclk", "hclk";
status = "disabled"; status = "disabled";
ep0 { ep0 {
@ -817,6 +1207,9 @@
compatible = "atmel,at91rm9200-ohci", "usb-ohci"; compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00600000 0x100000>; reg = <0x00600000 0x100000>;
interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>; interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&usb>, <&uhphs_clk>, <&udphs_clk>,
<&uhpck>;
clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
status = "disabled"; status = "disabled";
}; };
@ -824,6 +1217,8 @@
compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00700000 0x100000>; reg = <0x00700000 0x100000>;
interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>; interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&usb>, <&uhphs_clk>, <&uhpck>;
clock-names = "usb_clk", "ehci_clk", "uhpck";
status = "disabled"; status = "disabled";
}; };

View File

@ -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 { can0: can@f000c000 {
compatible = "atmel,at91sam9x5-can"; compatible = "atmel,at91sam9x5-can";
reg = <0xf000c000 0x300>; reg = <0xf000c000 0x300>;
interrupts = <40 IRQ_TYPE_LEVEL_HIGH 3>; interrupts = <40 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_can0_rx_tx>; pinctrl-0 = <&pinctrl_can0_rx_tx>;
clocks = <&can0_clk>;
clock-names = "can_clk";
status = "disabled"; status = "disabled";
}; };
@ -47,6 +65,8 @@
interrupts = <41 IRQ_TYPE_LEVEL_HIGH 3>; interrupts = <41 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_can1_rx_tx>; pinctrl-0 = <&pinctrl_can1_rx_tx>;
clocks = <&can1_clk>;
clock-names = "can_clk";
status = "disabled"; status = "disabled";
}; };
}; };

View File

@ -31,12 +31,23 @@
}; };
}; };
pmc: pmc@fffffc00 {
periphck {
macb1_clk: macb1_clk {
#clock-cells = <0>;
reg = <35>;
};
};
};
macb1: ethernet@f802c000 { macb1: ethernet@f802c000 {
compatible = "cdns,at32ap7000-macb", "cdns,macb"; compatible = "cdns,at32ap7000-macb", "cdns,macb";
reg = <0xf802c000 0x100>; reg = <0xf802c000 0x100>;
interrupts = <35 IRQ_TYPE_LEVEL_HIGH 3>; interrupts = <35 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_macb1_rmii>; pinctrl-0 = <&pinctrl_macb1_rmii>;
clocks = <&macb1_clk>, <&macb1_clk>;
clock-names = "hclk", "pclk";
status = "disabled"; status = "disabled";
}; };
}; };

View File

@ -64,12 +64,23 @@
}; };
}; };
pmc: pmc@fffffc00 {
periphck {
macb0_clk: macb0_clk {
#clock-cells = <0>;
reg = <34>;
};
};
};
macb0: ethernet@f0028000 { macb0: ethernet@f0028000 {
compatible = "cdns,pc302-gem", "cdns,gem"; compatible = "cdns,pc302-gem", "cdns,gem";
reg = <0xf0028000 0x100>; reg = <0xf0028000 0x100>;
interrupts = <34 IRQ_TYPE_LEVEL_HIGH 3>; interrupts = <34 IRQ_TYPE_LEVEL_HIGH 3>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_macb0_data_rgmii &pinctrl_macb0_signal_rgmii>; pinctrl-0 = <&pinctrl_macb0_data_rgmii &pinctrl_macb0_signal_rgmii>;
clocks = <&macb0_clk>, <&macb0_clk>;
clock-names = "hclk", "pclk";
status = "disabled"; status = "disabled";
}; };
}; };

View File

@ -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>;
};
};
};
}; };
}; };
}; };

View File

@ -9,6 +9,7 @@
#include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clk/at91.h>
/ { / {
ahb { ahb {
@ -30,6 +31,15 @@
}; };
}; };
pmc: pmc@fffffc00 {
periphck {
mci2_clk: mci2_clk {
#clock-cells = <0>;
reg = <23>;
};
};
};
mmc2: mmc@f8004000 { mmc2: mmc@f8004000 {
compatible = "atmel,hsmci"; compatible = "atmel,hsmci";
reg = <0xf8004000 0x600>; reg = <0xf8004000 0x600>;
@ -38,6 +48,8 @@
dma-names = "rxtx"; dma-names = "rxtx";
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc2_clk_cmd_dat0 &pinctrl_mmc2_dat1_3>; pinctrl-0 = <&pinctrl_mmc2_clk_cmd_dat0 &pinctrl_mmc2_dat1_3>;
clocks = <&mci2_clk>;
clock-names = "mci_clk";
status = "disabled"; status = "disabled";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;

View File

@ -9,6 +9,7 @@
#include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clk/at91.h>
/ { / {
aliases { aliases {
@ -17,10 +18,21 @@
ahb { ahb {
apb { apb {
pmc: pmc@fffffc00 {
periphck {
tcb1_clk: tcb1_clk {
#clock-cells = <0>;
reg = <27>;
};
};
};
tcb1: timer@f8014000 { tcb1: timer@f8014000 {
compatible = "atmel,at91sam9x5-tcb"; compatible = "atmel,at91sam9x5-tcb";
reg = <0xf8014000 0x100>; reg = <0xf8014000 0x100>;
interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&tcb1_clk>;
clock-names = "t0_clk";
}; };
}; };
}; };

View File

@ -9,8 +9,14 @@
#include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clk/at91.h>
/ { / {
aliases {
serial5 = &uart0;
serial6 = &uart1;
};
ahb { ahb {
apb { apb {
pinctrl@fffff200 { pinctrl@fffff200 {
@ -31,12 +37,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 { uart0: serial@f0024000 {
compatible = "atmel,at91sam9260-usart"; compatible = "atmel,at91sam9260-usart";
reg = <0xf0024000 0x200>; reg = <0xf0024000 0x200>;
interrupts = <16 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <16 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart0>; pinctrl-0 = <&pinctrl_uart0>;
clocks = <&uart0_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
@ -46,6 +70,8 @@
interrupts = <17 IRQ_TYPE_LEVEL_HIGH 5>; interrupts = <17 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>; pinctrl-0 = <&pinctrl_uart1>;
clocks = <&uart1_clk>;
clock-names = "usart";
status = "disabled"; status = "disabled";
}; };
}; };

View File

@ -18,17 +18,6 @@
reg = <0x20000000 0x20000000>; reg = <0x20000000 0x20000000>;
}; };
clocks {
#address-cells = <1>;
#size-cells = <1>;
ranges;
main_clock: clock@0 {
compatible = "atmel,osc", "fixed-clock";
clock-frequency = <12000000>;
};
};
ahb { ahb {
apb { apb {
spi0: spi@f0004000 { spi0: spi@f0004000 {
@ -38,6 +27,12 @@
macb0: ethernet@f0028000 { macb0: ethernet@f0028000 {
phy-mode = "rgmii"; phy-mode = "rgmii";
}; };
pmc: pmc@fffffc00 {
main: mainck {
clock-frequency = <12000000>;
};
};
}; };
nand0: nand@60000000 { nand0: nand@60000000 {

View File

@ -18,6 +18,7 @@
interrupts = <31 0x0>; interrupts = <31 0x0>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_qt1070_irq>; pinctrl-0 = <&pinctrl_qt1070_irq>;
wakeup-source;
}; };
}; };

View File

@ -1,15 +1,33 @@
if ARCH_AT91 if ARCH_AT91
config HAVE_AT91_UTMI
bool
config HAVE_AT91_USB_CLK
bool
config HAVE_AT91_DBGU0 config HAVE_AT91_DBGU0
bool bool
config HAVE_AT91_DBGU1 config HAVE_AT91_DBGU1
bool bool
config AT91_USE_OLD_CLK
bool
config AT91_PMC_UNIT config AT91_PMC_UNIT
bool bool
default !ARCH_AT91X40 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 config AT91_SAM9_ALT_RESET
bool bool
default !ARCH_AT91X40 default !ARCH_AT91X40
@ -21,6 +39,9 @@ config AT91_SAM9G45_RESET
config AT91_SAM9_TIME config AT91_SAM9_TIME
bool bool
config HAVE_AT91_SMD
bool
config SOC_AT91SAM9 config SOC_AT91SAM9
bool bool
select AT91_SAM9_TIME select AT91_SAM9_TIME
@ -65,6 +86,9 @@ config SOC_SAMA5D3
select SOC_SAMA5 select SOC_SAMA5
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select HAVE_AT91_DBGU1 select HAVE_AT91_DBGU1
select HAVE_AT91_UTMI
select HAVE_AT91_SMD
select HAVE_AT91_USB_CLK
help help
Select this if you are using one of Atmel's SAMA5D3 family SoC. Select this if you are using one of Atmel's SAMA5D3 family SoC.
This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
@ -78,11 +102,15 @@ config SOC_AT91RM9200
select HAVE_AT91_DBGU0 select HAVE_AT91_DBGU0
select MULTI_IRQ_HANDLER select MULTI_IRQ_HANDLER
select SPARSE_IRQ select SPARSE_IRQ
select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
config SOC_AT91SAM9260 config SOC_AT91SAM9260
bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20" bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20"
select HAVE_AT91_DBGU0 select HAVE_AT91_DBGU0
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
help help
Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE
or AT91SAM9G20 SoC. or AT91SAM9G20 SoC.
@ -92,6 +120,8 @@ config SOC_AT91SAM9261
select HAVE_AT91_DBGU0 select HAVE_AT91_DBGU0
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
help help
Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC. 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_AT91_DBGU1
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
config SOC_AT91SAM9RL config SOC_AT91SAM9RL
bool "AT91SAM9RL" bool "AT91SAM9RL"
select HAVE_AT91_DBGU0 select HAVE_AT91_DBGU0
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_UTMI
config SOC_AT91SAM9G45 config SOC_AT91SAM9G45
bool "AT91SAM9G45 or AT91SAM9M10 families" bool "AT91SAM9G45 or AT91SAM9M10 families"
select HAVE_AT91_DBGU1 select HAVE_AT91_DBGU1
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_UTMI
select HAVE_AT91_USB_CLK
help help
Select this if you are using one of Atmel's AT91SAM9G45 family SoC. Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
@ -121,6 +158,10 @@ config SOC_AT91SAM9X5
select HAVE_AT91_DBGU0 select HAVE_AT91_DBGU0
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_UTMI
select HAVE_AT91_SMD
select HAVE_AT91_USB_CLK
help help
Select this if you are using one of Atmel's AT91SAM9x5 family SoC. 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 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_AT91_DBGU0
select HAVE_FB_ATMEL select HAVE_FB_ATMEL
select SOC_AT91SAM9 select SOC_AT91SAM9
select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
help help
Select this if you are using Atmel's AT91SAM9N12 SoC. Select this if you are using Atmel's AT91SAM9N12 SoC.

View File

@ -12,26 +12,32 @@ config ARCH_AT91_NONE
config ARCH_AT91RM9200 config ARCH_AT91RM9200
bool "AT91RM9200" bool "AT91RM9200"
select SOC_AT91RM9200 select SOC_AT91RM9200
select AT91_USE_OLD_CLK
config ARCH_AT91SAM9260 config ARCH_AT91SAM9260
bool "AT91SAM9260 or AT91SAM9XE or AT91SAM9G20" bool "AT91SAM9260 or AT91SAM9XE or AT91SAM9G20"
select SOC_AT91SAM9260 select SOC_AT91SAM9260
select AT91_USE_OLD_CLK
config ARCH_AT91SAM9261 config ARCH_AT91SAM9261
bool "AT91SAM9261 or AT91SAM9G10" bool "AT91SAM9261 or AT91SAM9G10"
select SOC_AT91SAM9261 select SOC_AT91SAM9261
select AT91_USE_OLD_CLK
config ARCH_AT91SAM9263 config ARCH_AT91SAM9263
bool "AT91SAM9263" bool "AT91SAM9263"
select SOC_AT91SAM9263 select SOC_AT91SAM9263
select AT91_USE_OLD_CLK
config ARCH_AT91SAM9RL config ARCH_AT91SAM9RL
bool "AT91SAM9RL" bool "AT91SAM9RL"
select SOC_AT91SAM9RL select SOC_AT91SAM9RL
select AT91_USE_OLD_CLK
config ARCH_AT91SAM9G45 config ARCH_AT91SAM9G45
bool "AT91SAM9G45" bool "AT91SAM9G45"
select SOC_AT91SAM9G45 select SOC_AT91SAM9G45
select AT91_USE_OLD_CLK
config ARCH_AT91X40 config ARCH_AT91X40
bool "AT91x40" bool "AT91x40"

View File

@ -7,7 +7,7 @@ obj-m :=
obj-n := obj-n :=
obj- := 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_SAM9_ALT_RESET) += at91sam9_alt_reset.o
obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o
obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o

View File

@ -12,13 +12,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <mach/at91rm9200.h> #include <mach/at91rm9200.h>
#include <mach/at91_pmc.h>
#include <mach/at91_st.h> #include <mach/at91_st.h>
#include <mach/cpu.h> #include <mach/cpu.h>

View File

@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -20,7 +21,6 @@
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/at91_dbgu.h> #include <mach/at91_dbgu.h>
#include <mach/at91sam9260.h> #include <mach/at91sam9260.h>
#include <mach/at91_pmc.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h" #include "at91_rstc.h"

View File

@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -19,7 +20,6 @@
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/at91sam9261.h> #include <mach/at91sam9261.h>
#include <mach/at91_pmc.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h" #include "at91_rstc.h"

View File

@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -18,7 +19,6 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <mach/at91sam9263.h> #include <mach/at91sam9263.h>
#include <mach/at91_pmc.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h" #include "at91_rstc.h"

View File

@ -39,6 +39,7 @@
static u32 pit_cycle; /* write-once */ static u32 pit_cycle; /* write-once */
static u32 pit_cnt; /* access only w/system irq blocked */ static u32 pit_cnt; /* access only w/system irq blocked */
static void __iomem *pit_base_addr __read_mostly; static void __iomem *pit_base_addr __read_mostly;
static struct clk *mck;
static inline unsigned int pit_read(unsigned int reg_offset) 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) if (!pit_base_addr)
goto node_err; goto node_err;
mck = of_clk_get(np, 0);
/* Get the interrupts property */ /* Get the interrupts property */
ret = irq_of_parse_and_map(np, 0); ret = irq_of_parse_and_map(np, 0);
if (!ret) { if (!ret) {
pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); pr_crit("AT91: PIT: Unable to get IRQ from DT\n");
if (!IS_ERR(mck))
clk_put(mck);
goto ioremap_err; goto ioremap_err;
} }
at91sam926x_pit_irq.irq = ret; at91sam926x_pit_irq.irq = ret;
@ -230,6 +235,8 @@ void __init at91sam926x_pit_init(void)
unsigned bits; unsigned bits;
int ret; int ret;
mck = ERR_PTR(-ENOENT);
/* For device tree enabled device: initialize here */ /* For device tree enabled device: initialize here */
of_at91sam926x_pit_init(); 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 * Use our actual MCK to figure out how many MCK/16 ticks per
* 1/HZ period (instead of a compile-time constant LATCH). * 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; pit_cycle = (pit_rate + HZ/2) / HZ;
WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);

View File

@ -12,13 +12,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <mach/at91sam9g45.h> #include <mach/at91sam9g45.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include "at91_aic.h" #include "at91_aic.h"

View File

@ -8,12 +8,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <mach/at91sam9n12.h> #include <mach/at91sam9n12.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include "board.h" #include "board.h"

View File

@ -10,6 +10,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -19,7 +20,6 @@
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/at91_dbgu.h> #include <mach/at91_dbgu.h>
#include <mach/at91sam9rl.h> #include <mach/at91sam9rl.h>
#include <mach/at91_pmc.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h" #include "at91_rstc.h"

View File

@ -8,12 +8,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <mach/at91sam9x5.h> #include <mach/at91sam9x5.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include "board.h" #include "board.h"

View File

@ -16,6 +16,7 @@
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/clk-provider.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -26,6 +27,13 @@
#include "at91_aic.h" #include "at91_aic.h"
#include "generic.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 = { 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)") DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = sama5_dt_timer_init,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic5_handle_irq, .handle_irq = at91_aic5_handle_irq,
.init_early = at91_dt_initialize, .init_early = at91_dt_initialize,

View File

@ -24,9 +24,9 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/clk/at91_pmc.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
@ -884,6 +884,11 @@ static int __init at91_pmc_init(unsigned long main_clock)
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
static struct of_device_id pmc_ids[] = { static struct of_device_id pmc_ids[] = {
{ .compatible = "atmel,at91rm9200-pmc" }, { .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*/ } { /*sentinel*/ }
}; };

View File

@ -46,11 +46,12 @@ extern void at91sam926x_pit_init(void);
extern void at91x40_timer_init(void); extern void at91x40_timer_init(void);
/* Clocks */ /* 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_clock_init(unsigned long main_clock);
extern int __init at91_dt_clock_init(void); extern int __init at91_dt_clock_init(void);
#else #else
static int inline at91_clock_init(unsigned long main_clock) { return 0; } static int inline at91_clock_init(unsigned long main_clock) { return 0; }
static int inline at91_dt_clock_init(void) { return 0; }
#endif #endif
struct device; struct device;

View File

@ -19,13 +19,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include "at91_aic.h" #include "at91_aic.h"

View File

@ -13,8 +13,8 @@
*/ */
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/clk/at91_pmc.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <mach/at91_pmc.h>
#include <mach/at91_ramc.h> #include <mach/at91_ramc.h>

View File

@ -9,359 +9,18 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk/at91_pmc.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <mach/sama5d3.h> #include <mach/sama5d3.h>
#include <mach/at91_pmc.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "clock.h"
#include "sam9_smc.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_DIV2,
};
static struct clk twi1_clk = {
.name = "twi1_clk",
.pid = SAMA5D3_ID_TWI1,
.type = CLK_TYPE_PERIPHERAL,
.div = AT91_PMC_PCR_DIV2,
};
static struct clk twi2_clk = {
.name = "twi2_clk",
.pid = SAMA5D3_ID_TWI2,
.type = CLK_TYPE_PERIPHERAL,
.div = AT91_PMC_PCR_DIV2,
};
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 * AT91SAM9x5 processor initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
@ -378,6 +37,5 @@ static void __init sama5d3_initialize(void)
AT91_SOC_START(sama5d3) AT91_SOC_START(sama5d3)
.map_io = sama5d3_map_io, .map_io = sama5d3_map_io,
.register_clocks = sama5d3_register_clocks,
.init = sama5d3_initialize, .init = sama5d3_initialize,
AT91_SOC_END AT91_SOC_END

View File

@ -11,6 +11,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/pinctrl/machine.h> #include <linux/pinctrl/machine.h>
#include <linux/clk/at91_pmc.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
@ -18,7 +19,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/at91_dbgu.h> #include <mach/at91_dbgu.h>
#include <mach/at91_pmc.h>
#include "at91_shdwc.h" #include "at91_shdwc.h"
#include "soc.h" #include "soc.h"
@ -491,6 +491,7 @@ void __init at91rm9200_dt_initialize(void)
at91_dt_clock_init(); at91_dt_clock_init();
/* Register the processor-specific clocks */ /* Register the processor-specific clocks */
if (at91_boot_soc.register_clocks)
at91_boot_soc.register_clocks(); at91_boot_soc.register_clocks();
at91_boot_soc.init(); at91_boot_soc.init();
@ -506,6 +507,7 @@ void __init at91_dt_initialize(void)
at91_dt_clock_init(); at91_dt_clock_init();
/* Register the processor-specific clocks */ /* Register the processor-specific clocks */
if (at91_boot_soc.register_clocks)
at91_boot_soc.register_clocks(); at91_boot_soc.register_clocks();
if (at91_boot_soc.init) if (at91_boot_soc.init)

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_X86) += x86/

12
drivers/clk/at91/Makefile Normal file
View File

@ -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

187
drivers/clk/at91/clk-main.c Normal file
View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

531
drivers/clk/at91/clk-pll.c Normal file
View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

171
drivers/clk/at91/clk-smd.c Normal file
View File

@ -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);
}

View File

@ -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);
}

398
drivers/clk/at91/clk-usb.c Normal file
View File

@ -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);
}

159
drivers/clk/at91/clk-utmi.c Normal file
View File

@ -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);
}

397
drivers/clk/at91/pmc.c Normal file
View File

@ -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);

116
drivers/clk/at91/pmc.h Normal file
View File

@ -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_ */

View File

@ -326,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
#if defined(CONFIG_ARCH_AT91SAM9RL) #if defined(CONFIG_ARCH_AT91SAM9RL)
#include <mach/at91_pmc.h> #include <linux/clk/at91_pmc.h>
static void toggle_bias(int is_on) static void toggle_bias(int is_on)
{ {

View File

@ -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

View File

@ -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) 2005 Ivan Kokshaysky
* Copyright (C) SAN People * 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_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */ #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_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */
#define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */ #define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */
#define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */ #define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */