Commit f8da810c authored by Arnd Bergmann's avatar Arnd Bergmann

Merge branch 'tegra/clk' into next/dt2

This is a dependency for the tegra/dt branch.

Conflicts:
	drivers/clocksource/tegra20_timer.c
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 03e86b3a 964ea475
Frequently asked questions about the sunxi clock system
=======================================================
This document contains useful bits of information that people tend to ask
about the sunxi clock system, as well as accompanying ASCII art when adequate.
Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the
system?
A: The 24MHz oscillator allows gating to save power. Indeed, if gated
carelessly the system would stop functioning, but with the right
steps, one can gate it and keep the system running. Consider this
simplified suspend example:
While the system is operational, you would see something like
24MHz 32kHz
|
PLL1
\
\_ CPU Mux
|
[CPU]
When you are about to suspend, you switch the CPU Mux to the 32kHz
oscillator:
24Mhz 32kHz
| |
PLL1 |
/
CPU Mux _/
|
[CPU]
Finally you can gate the main oscillator
32kHz
|
|
/
CPU Mux _/
|
[CPU]
Q: Were can I learn more about the sunxi clocks?
A: The linux-sunxi wiki contains a page documenting the clock registers,
you can find it at
http://linux-sunxi.org/A10/CCM
The authoritative source for information at this time is the ccmu driver
released by Allwinner, you can find it at
https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu
......@@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw)
};
Below is a matrix detailing which clk_ops are mandatory based upon the
hardware capbilities of that clock. A cell marked as "y" means
hardware capabilities of that clock. A cell marked as "y" means
mandatory, a cell marked as "n" implies that either including that
callback is invalid or otherwise uneccesary. Empty cells are either
callback is invalid or otherwise unnecessary. Empty cells are either
optional or must be evaluated on a case-by-case basis.
clock hardware characteristics
......
NVIDIA Tegra Power Management Controller (PMC)
Properties:
The PMC block interacts with an external Power Management Unit. The PMC
mostly controls the entry and exit of the system from different sleep
modes. It provides power-gating controllers for SoC and CPU power-islands.
Required properties:
- name : Should be pmc
- compatible : Should contain "nvidia,tegra<chip>-pmc".
- reg : Offset and length of the register set for the device
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : Must include the following entries:
"pclk" (The Tegra clock of that name),
"clk32k_in" (The 32KHz clock input to Tegra).
Optional properties:
- nvidia,invert-interrupt : If present, inverts the PMU interrupt signal.
The PMU is an external Power Management Unit, whose interrupt output
signal is fed into the PMC. This signal is optionally inverted, and then
fed into the ARM GIC. The PMC is not involved in the detection or
handling of this interrupt signal, merely its inversion.
- nvidia,suspend-mode : The suspend mode that the platform should use.
Valid values are 0, 1 and 2:
0 (LP0): CPU + Core voltage off and DRAM in self-refresh
1 (LP1): CPU voltage off and DRAM in self-refresh
2 (LP2): CPU voltage off
- nvidia,core-power-req-active-high : Boolean, core power request active-high
- nvidia,sys-clock-req-active-high : Boolean, system clock request active-high
- nvidia,combined-power-req : Boolean, combined power request for CPU & Core
- nvidia,cpu-pwr-good-en : Boolean, CPU power good signal (from PMIC to PMC)
is enabled.
Required properties when nvidia,suspend-mode is specified:
- nvidia,cpu-pwr-good-time : CPU power good time in uS.
- nvidia,cpu-pwr-off-time : CPU power off time in uS.
- nvidia,core-pwr-good-time : <Oscillator-stable-time Power-stable-time>
Core power good time in uS.
- nvidia,core-pwr-off-time : Core power off time in uS.
Required properties when nvidia,suspend-mode=<0>:
- nvidia,lp0-vec : <start length> Starting address and length of LP0 vector
The LP0 vector contains the warm boot code that is executed by AVP when
resuming from the LP0 state. The AVP (Audio-Video Processor) is an ARM7
processor and always being the first boot processor when chip is power on
or resume from deep sleep mode. When the system is resumed from the deep
sleep mode, the warm boot code will restore some PLLs, clocks and then
bring up CPU0 for resuming the system.
Example:
/ SoC dts including file
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
clocks = <&tegra_car 110>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
nvidia,invert-interrupt;
nvidia,suspend-mode = <1>;
nvidia,cpu-pwr-good-time = <2000>;
nvidia,cpu-pwr-off-time = <100>;
nvidia,core-pwr-good-time = <3845 3845>;
nvidia,core-pwr-off-time = <458>;
nvidia,core-power-req-active-high;
nvidia,sys-clock-req-active-high;
nvidia,lp0-vec = <0xbdffd000 0x2000>;
};
/ Tegra board dts file
{
...
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
...
};
Binding for the axi-clkgen clock generator
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be "adi,axi-clkgen".
- #clock-cells : from common clock binding; Should always be set to 0.
- reg : Address and length of the axi-clkgen register set.
- clocks : Phandle and clock specifier for the parent clock.
Optional properties:
- clock-output-names : From common clock binding.
Example:
clock@0xff000000 {
compatible = "adi,axi-clkgen";
#clock-cells = <0>;
reg = <0xff000000 0x1000>;
clocks = <&osc 1>;
};
NVIDIA Tegra114 Clock And Reset Controller
This binding uses the common clock binding:
Documentation/devicetree/bindings/clock/clock-bindings.txt
The CAR (Clock And Reset) Controller on Tegra is the HW module responsible
for muxing and gating Tegra's clocks, and setting their rates.
Required properties :
- compatible : Should be "nvidia,tegra114-car"
- reg : Should contain CAR registers location and length
- clocks : Should contain phandle and clock specifiers for two clocks:
the 32 KHz "32k_in", and the board-specific oscillator "osc".
- #clock-cells : Should be 1.
In clock consumers, this cell represents the clock ID exposed by the CAR.
The first 160 clocks are numbered to match the bits in the CAR's CLK_OUT_ENB
registers. These IDs often match those in the CAR's RST_DEVICES registers,
but not in all cases. Some bits in CLK_OUT_ENB affect multiple clocks. In
this case, those clocks are assigned IDs above 160 in order to highlight
this issue. Implementations that interpret these clock IDs as bit values
within the CLK_OUT_ENB or RST_DEVICES registers should be careful to
explicitly handle these special cases.
The balance of the clocks controlled by the CAR are assigned IDs of 160 and
above.
0 unassigned
1 unassigned
2 unassigned
3 unassigned
4 rtc
5 timer
6 uarta
7 unassigned (register bit affects uartb and vfir)
8 unassigned
9 sdmmc2
10 unassigned (register bit affects spdif_in and spdif_out)
11 i2s1
12 i2c1
13 ndflash
14 sdmmc1
15 sdmmc4
16 unassigned
17 pwm
18 i2s2
19 epp
20 unassigned (register bit affects vi and vi_sensor)
21 2d
22 usbd
23 isp
24 3d
25 unassigned
26 disp2
27 disp1
28 host1x
29 vcp
30 i2s0
31 unassigned
32 unassigned
33 unassigned
34 apbdma
35 unassigned
36 kbc
37 unassigned
38 unassigned
39 unassigned (register bit affects fuse and fuse_burn)
40 kfuse
41 sbc1
42 nor
43 unassigned
44 sbc2
45 unassigned
46 sbc3
47 i2c5
48 dsia
49 unassigned
50 mipi
51 hdmi
52 csi
53 unassigned
54 i2c2
55 uartc
56 mipi-cal
57 emc
58 usb2
59 usb3
60 msenc
61 vde
62 bsea
63 bsev
64 unassigned
65 uartd
66 unassigned
67 i2c3
68 sbc4
69 sdmmc3
70 unassigned
71 owr
72 afi
73 csite
74 unassigned
75 unassigned
76 la
77 trace
78 soc_therm
79 dtv
80 ndspeed
81 i2cslow
82 dsib
83 tsec
84 unassigned
85 unassigned
86 unassigned
87 unassigned
88 unassigned
89 xusb_host
90 unassigned
91 msenc
92 csus
93 unassigned
94 unassigned
95 unassigned (bit affects xusb_dev and xusb_dev_src)
96 unassigned
97 unassigned
98 unassigned
99 mselect
100 tsensor
101 i2s3
102 i2s4
103 i2c4
104 sbc5
105 sbc6
106 d_audio
107 apbif
108 dam0
109 dam1
110 dam2
111 hda2codec_2x
112 unassigned
113 audio0_2x
114 audio1_2x
115 audio2_2x
116 audio3_2x
117 audio4_2x
118 spdif_2x
119 actmon
120 extern1
121 extern2
122 extern3
123 unassigned
124 unassigned
125 hda
126 unassigned
127 se
128 hda2hdmi
129 unassigned
130 unassigned
131 unassigned
132 unassigned
133 unassigned
134 unassigned
135 unassigned
136 unassigned
137 unassigned
138 unassigned
139 unassigned
140 unassigned
141 unassigned
142 unassigned
143 unassigned (bit affects xusb_falcon_src, xusb_fs_src,
xusb_host_src and xusb_ss_src)
144 cilab
145 cilcd
146 cile
147 dsialp
148 dsiblp
149 unassigned
150 dds
151 unassigned
152 dp2
153 amx
154 adx
155 unassigned (bit affects dfll_ref and dfll_soc)
156 xusb_ss
192 uartb
193 vfir
194 spdif_in
195 spdif_out
196 vi
197 vi_sensor
198 fuse
199 fuse_burn
200 clk_32k
201 clk_m
202 clk_m_div2
203 clk_m_div4
204 pll_ref
205 pll_c
206 pll_c_out1
207 pll_c2
208 pll_c3
209 pll_m
210 pll_m_out1
211 pll_p
212 pll_p_out1
213 pll_p_out2
214 pll_p_out3
215 pll_p_out4
216 pll_a
217 pll_a_out0
218 pll_d
219 pll_d_out0
220 pll_d2
221 pll_d2_out0
222 pll_u
223 pll_u_480M
224 pll_u_60M
225 pll_u_48M
226 pll_u_12M
227 pll_x
228 pll_x_out0
229 pll_re_vco
230 pll_re_out
231 pll_e_out0
232 spdif_in_sync
233 i2s0_sync
234 i2s1_sync
235 i2s2_sync
236 i2s3_sync
237 i2s4_sync
238 vimclk_sync
239 audio0
240 audio1
241 audio2
242 audio3
243 audio4
244 spdif
245 clk_out_1
246 clk_out_2
247 clk_out_3
248 blink
252 xusb_host_src
253 xusb_falcon_src
254 xusb_fs_src
255 xusb_ss_src
256 xusb_dev_src
257 xusb_dev
258 xusb_hs_src
259 sclk
260 hclk
261 pclk
262 cclk_g
263 cclk_lp
264 dfll_ref
265 dfll_soc
Example SoC include file:
/ {
tegra_car: clock {
compatible = "nvidia,tegra114-car";
reg = <0x60006000 0x1000>;
#clock-cells = <1>;
};
usb@c5004000 {
clocks = <&tegra_car 58>; /* usb2 */
};
};
Example board file:
/ {
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
osc: clock@0 {
compatible = "fixed-clock";
reg = <0>;
#clock-cells = <0>;
clock-frequency = <12000000>;
};
clk_32k: clock@1 {
compatible = "fixed-clock";
reg = <1>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
&tegra_car {
clocks = <&clk_32k> <&osc>;
};
};
......@@ -120,8 +120,8 @@ Required properties :
90 clk_d
91 unassigned
92 sus
93 cdev1
94 cdev2
93 cdev2
94 cdev1
95 unassigned
96 uart2
......
Device Tree Clock bindings for arch-sunxi
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be one of the following:
"allwinner,sun4i-osc-clk" - for a gatable oscillator
"allwinner,sun4i-pll1-clk" - for the main PLL clock
"allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
"allwinner,sun4i-axi-clk" - for the AXI clock
"allwinner,sun4i-ahb-clk" - for the AHB clock
"allwinner,sun4i-apb0-clk" - for the APB0 clock
"allwinner,sun4i-apb1-clk" - for the APB1 clock
"allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing
Required properties for all clocks:
- reg : shall be the control register address for the clock.
- clocks : shall be the input parent clock(s) phandle for the clock
- #clock-cells : from common clock binding; shall be set to 0.
For example:
osc24M: osc24M@01c20050 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-osc-clk";
reg = <0x01c20050 0x4>;
clocks = <&osc24M_fixed>;
};
pll1: pll1@01c20000 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
};
cpu: cpu@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>;
};
......@@ -675,6 +675,7 @@ config ARCH_TEGRA
select HAVE_CLK
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
select SOC_BUS
select SPARSE_IRQ
select USE_OF
help
......
......@@ -12,10 +12,22 @@ memory {
serial@70006300 {
status = "okay";
clock-frequency = <408000000>;
};
pmc {
nvidia,invert-interrupt;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
};
......@@ -12,10 +12,22 @@ memory {
serial@70006300 {
status = "okay";
clock-frequency = <408000000>;
};
pmc {
nvidia,invert-interrupt;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
};
......@@ -24,10 +24,11 @@ timer@60005000 {
0 42 0x04
0 121 0x04
0 122 0x04>;
clocks = <&tegra_car 5>;
};
tegra_car: clock {
compatible = "nvidia,tegra114-car, nvidia,tegra30-car";
compatible = "nvidia,tegra114-car";
reg = <0x60006000 0x1000>;
#clock-cells = <1>;
};
......@@ -66,6 +67,7 @@ serial@70006000 {
reg-shift = <2>;
interrupts = <0 36 0x04>;
status = "disabled";
clocks = <&tegra_car 6>;
};
serial@70006040 {
......@@ -74,6 +76,7 @@ serial@70006040 {
reg-shift = <2>;
interrupts = <0 37 0x04>;
status = "disabled";
clocks = <&tegra_car 192>;
};
serial@70006200 {
......@@ -82,6 +85,7 @@ serial@70006200 {
reg-shift = <2>;
interrupts = <0 46 0x04>;
status = "disabled";
clocks = <&tegra_car 55>;
};
serial@70006300 {
......@@ -90,17 +94,21 @@ serial@70006300 {
reg-shift = <2>;
interrupts = <0 90 0x04>;
status = "disabled";
clocks = <&tegra_car 65>;
};
rtc {
compatible = "nvidia,tegra114-rtc", "nvidia,tegra20-rtc";
reg = <0x7000e000 0x100>;
interrupts = <0 2 0x04>;
clocks = <&tegra_car 4>;
};
pmc {
compatible = "nvidia,tegra114-pmc", "nvidia,tegra30-pmc";
compatible = "nvidia,tegra114-pmc";
reg = <0x7000e400 0x400>;
clocks = <&tegra_car 261>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
};
iommu {
......
......@@ -444,7 +444,20 @@ usb@c5004000 {
};
sdhci@c8000600 {
cd-gpios = <&gpio 23 0>; /* gpio PC7 */
cd-gpios = <&gpio 23 1>; /* gpio PC7 */
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
sound {
......
......@@ -437,7 +437,7 @@ usb-phy@c5004400 {
sdhci@c8000200 {
status = "okay";
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 155 0>; /* gpio PT3 */
bus-width = <4>;
......@@ -445,12 +445,25 @@ sdhci@c8000200 {
sdhci@c8000600 {
status = "okay";
cd-gpios = <&gpio 58 0>; /* gpio PH2 */
cd-gpios = <&gpio 58 1>; /* gpio PH2 */
wp-gpios = <&gpio 59 0>; /* gpio PH3 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
kbc {
status = "okay";
nvidia,debounce-delay-ms = <2>;
......
......@@ -436,7 +436,7 @@ usb-phy@c5004400 {
sdhci@c8000000 {
status = "okay";
cd-gpios = <&gpio 173 0>; /* gpio PV5 */
cd-gpios = <&gpio 173 1>; /* gpio PV5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 169 0>; /* gpio PV1 */
bus-width = <4>;
......@@ -447,6 +447,19 @@ sdhci@c8000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
gpio-keys {
compatible = "gpio-keys";
......
......@@ -584,7 +584,7 @@ sdhci@c8000000 {
sdhci@c8000400 {
status = "okay";
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
bus-width = <4>;
......@@ -595,6 +595,19 @@ sdhci@c8000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
gpio-keys {
compatible = "gpio-keys";
......
......@@ -465,12 +465,25 @@ usb@c5008000 {
};
sdhci@c8000600 {
cd-gpios = <&gpio 58 0>; /* gpio PH2 */
cd-gpios = <&gpio 58 1>; /* gpio PH2 */
wp-gpios = <&gpio 59 0>; /* gpio PH3 */
bus-width = <4>;
status = "okay";
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
regulators {
compatible = "simple-bus";
......
......@@ -325,11 +325,24 @@ sdhci@c8000000 {
sdhci@c8000600 {
status = "okay";
cd-gpios = <&gpio 121 0>; /* gpio PP1 */
cd-gpios = <&gpio 121 1>; /* gpio PP1 */
wp-gpios = <&gpio 122 0>; /* gpio PP2 */
bus-width = <4>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
poweroff {
compatible = "gpio-poweroff";
gpios = <&gpio 191 1>; /* gpio PX7, active low */
......
......@@ -520,7 +520,7 @@ sdhci@c8000000 {
sdhci@c8000400 {
status = "okay";
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
bus-width = <4>;
......@@ -531,6 +531,19 @@ sdhci@c8000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
regulators {
compatible = "simple-bus";
#address-cells = <1>;
......
......@@ -510,6 +510,7 @@ usb@c5008000 {
sdhci@c8000400 {
status = "okay";
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 173 0>; /* gpio PV5 */
bus-width = <8>;
};
......@@ -519,6 +520,19 @@ sdhci@c8000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
kbc {
status = "okay";
nvidia,debounce-delay-ms = <20>;
......
......@@ -145,6 +145,7 @@ timer@60005000 {
0 1 0x04
0 41 0x04
0 42 0x04>;
clocks = <&tegra_car 5>;
};
tegra_car: clock {
......@@ -304,6 +305,7 @@ rtc {
compatible = "nvidia,tegra20-rtc";
reg = <0x7000e000 0x100>;
interrupts = <0 2 0x04>;
clocks = <&tegra_car 4>;
};
i2c@7000c000 {
......@@ -416,6 +418,8 @@ kbc {
pmc {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
clocks = <&tegra_car 110>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
};
memory-controller@7000f000 {
......
......@@ -257,7 +257,7 @@ pmc {
sdhci@78000000 {
status = "okay";
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 155 0>; /* gpio PT3 */
power-gpios = <&gpio 31 0>; /* gpio PD7 */
bus-width = <4>;
......@@ -268,6 +268,19 @@ sdhci@78000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
regulators {
compatible = "simple-bus";
#address-cells = <1>;
......
......@@ -311,7 +311,7 @@ pmc {
sdhci@78000000 {
status = "okay";
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
wp-gpios = <&gpio 155 0>; /* gpio PT3 */
power-gpios = <&gpio 31 0>; /* gpio PD7 */
bus-width = <4>;
......@@ -322,6 +322,19 @@ sdhci@78000600 {
bus-width = <8>;
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk32k_in: clock {
compatible = "fixed-clock";
reg=<0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
regulators {
compatible = "simple-bus";
#address-cells = <1>;
......
......@@ -148,6 +148,7 @@ timer@60005000 {
0 42 0x04
0 121 0x04
0 122 0x04>;
clocks = <&tegra_car 5>;
};
tegra_car: clock {
......@@ -291,6 +292,7 @@ rtc {
compatible = "nvidia,tegra30-rtc", "nvidia,tegra20-rtc";
reg = <0x7000e000 0x100>;
interrupts = <0 2 0x04>;
clocks = <&tegra_car 4>;
};
i2c@7000c000 {
......@@ -423,8 +425,10 @@ kbc {
};
pmc {
compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc";
compatible = "nvidia,tegra30-pmc";
reg = <0x7000e400 0x400>;
clocks = <&tegra_car 218>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
};
memory-controller {
......
......@@ -169,7 +169,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
busy->mux.reg = reg;
busy->mux.shift = shift;
busy->mux.width = width;
busy->mux.mask = BIT(width) - 1;
busy->mux.lock = &imx_ccm_lock;
busy->mux_ops = &clk_mux_ops;
......
......@@ -10,6 +10,7 @@ obj-y += pm.o
obj-y += reset.o
obj-y += reset-handler.o
obj-y += sleep.o
obj-y += tegra.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
......@@ -27,9 +28,7 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += board-dt-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o
endif
......
/*
* NVIDIA Tegra114 device tree board support
*
* Copyright (C) 2013 NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clocksource.h>
#include <asm/mach/arch.h>
#include "board.h"
#include "common.h"
static void __init tegra114_dt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
static const char * const tegra114_dt_board_compat[] = {
"nvidia,tegra114",
NULL,
};
DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)")
.smp = smp_ops(tegra_smp_ops),
.map_io = tegra_map_common_io,
.init_early = tegra114_init_early,
.init_irq = tegra_dt_init_irq,
.init_time = clocksource_of_init,
.init_machine = tegra114_dt_init,
.init_late = tegra_init_late,
.restart = tegra_assert_system_reset,
.dt_compat = tegra114_dt_board_compat,
MACHINE_END
/*
* arch/arm/mach-tegra/board-dt-tegra30.c
*
* NVIDIA Tegra30 device tree board support
*
* Copyright (C) 2011 NVIDIA Corporation
*
* Derived from:
*
* arch/arm/mach-tegra/board-dt-tegra20.c
*
* Copyright (C) 2010 Secret Lab Technologies, Ltd.
* Copyright (C) 2010 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clocksource.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <asm/mach/arch.h>
#include "board.h"
#include "common.h"
#include "iomap.h"
static void __init tegra30_dt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
static const char *tegra30_dt_board_compat[] = {
"nvidia,tegra30",
NULL
};
DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)")
.smp = smp_ops(tegra_smp_ops),
.map_io = tegra_map_common_io,
.init_early = tegra30_init_early,
.init_irq = tegra_dt_init_irq,
.init_time = clocksource_of_init,
.init_machine = tegra30_dt_init,
.init_late = tegra_init_late,
.restart = tegra_assert_system_reset,
.dt_compat = tegra30_dt_board_compat,
MACHINE_END
......@@ -62,7 +62,11 @@ int __init harmony_pcie_init(void)
goto err_reg;
}
regulator_enable(regulator);
err = regulator_enable(regulator);
if (err) {
pr_err("%s: regulator_enable failed: %d\n", __func__, err);
goto err_en;
}
err = tegra_pcie_init(true, true);
if (err) {
......@@ -74,6 +78,7 @@ int __init harmony_pcie_init(void)
err_pcie:
regulator_disable(regulator);
err_en:
regulator_put(regulator);
err_reg:
gpio_free(en_vdd_1v05);
......
......@@ -26,9 +26,7 @@
void tegra_assert_system_reset(char mode, const char *cmd);
void __init tegra20_init_early(void);
void __init tegra30_init_early(void);
void __init tegra114_init_early(void);
void __init tegra_init_early(void);
void __init tegra_map_common_io(void);
void __init tegra_init_irq(void);
void __init tegra_dt_init_irq(void);
......
......@@ -33,6 +33,7 @@
#include "common.h"
#include "fuse.h"
#include "iomap.h"
#include "irq.h"
#include "pmc.h"
#include "apbio.h"
#include "sleep.h"
......@@ -61,8 +62,10 @@ u32 tegra_uart_config[4] = {
void __init tegra_dt_init_irq(void)
{
tegra_clocks_init();
tegra_pmc_init();
tegra_init_irq();
irqchip_init();
tegra_legacy_irq_syscore_init();
}
#endif
......@@ -94,40 +97,18 @@ static void __init tegra_init_cache(void)
}
static void __init tegra_init_early(void)
void __init tegra_init_early(void)
{
tegra_cpu_reset_handler_init();
tegra_apb_io_init();
tegra_init_fuse();
tegra_init_cache();
tegra_pmc_init();
tegra_powergate_init();
tegra_hotplug_init();
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
void __init tegra20_init_early(void)
{
tegra_init_early();
tegra20_hotplug_init();
}
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
void __init tegra30_init_early(void)
{
tegra_init_early();
tegra30_hotplug_init();
}
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
void __init tegra114_init_early(void)
{
tegra_init_early();
}
#endif
void __init tegra_init_late(void)
{
tegra_init_suspend();
tegra_powergate_debugfs_init();
}
......@@ -130,10 +130,6 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct cpuidle_state *state = &drv->states[index];
u32 cpu_on_time = state->exit_latency;
u32 cpu_off_time = state->target_residency - state->exit_latency;
while (tegra20_cpu_is_resettable_soon())
cpu_relax();
......@@ -142,7 +138,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
tegra_idle_lp2_last();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
......
......@@ -72,10 +72,6 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct cpuidle_state *state = &drv->states[index];
u32 cpu_on_time = state->exit_latency;
u32 cpu_off_time = state->target_residency - state->exit_latency;
/* All CPUs entering LP2 is not working.
* Don't let CPU0 enter LP2 when any secondary CPU is online.
*/
......@@ -86,7 +82,7 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
tegra_idle_lp2_last();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
......@@ -102,12 +98,8 @@ static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
smp_wmb();
save_cpu_arch_register();
cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
restore_cpu_arch_register();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
return true;
......
......@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/fuse.c
*
* Copyright (C) 2010 Google, Inc.
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@android.com>
......@@ -137,6 +138,9 @@ void tegra_init_fuse(void)
tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT;
tegra_init_speedo_data = &tegra30_init_speedo_data;
break;
case TEGRA114:
tegra_init_speedo_data = &tegra114_init_speedo_data;
break;
default:
pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id);
tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT;
......
/*
* Copyright (C) 2010 Google, Inc.
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@android.com>
......@@ -66,4 +67,10 @@ void tegra30_init_speedo_data(void);
static inline void tegra30_init_speedo_data(void) {}
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
void tegra114_init_speedo_data(void);
#else
static inline void tegra114_init_speedo_data(void) {}
#endif
#endif
......@@ -7,8 +7,5 @@
ENTRY(tegra_secondary_startup)
bl v7_invalidate_l1
/* Enable coresight */
mov32 r0, 0xC5ACCE55
mcr p14, 0, r0, c7, c12, 6
b secondary_startup
ENDPROC(tegra_secondary_startup)
/*
*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
* Copyright (c) 2010, 2012 NVIDIA Corporation. All rights reserved.
* Copyright (c) 2010, 2012-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -15,6 +14,7 @@
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include "fuse.h"
#include "sleep.h"
static void (*tegra_hotplug_shutdown)(void);
......@@ -56,18 +56,13 @@ int tegra_cpu_disable(unsigned int cpu)
return cpu == 0 ? -EPERM : 0;
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
extern void tegra20_hotplug_shutdown(void);
void __init tegra20_hotplug_init(void)
void __init tegra_hotplug_init(void)
{
tegra_hotplug_shutdown = tegra20_hotplug_shutdown;
}
#endif
if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
return;
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
extern void tegra30_hotplug_shutdown(void);
void __init tegra30_hotplug_init(void)
{
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20)
tegra_hotplug_shutdown = tegra20_hotplug_shutdown;
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30)
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
}
#endif
......@@ -4,7 +4,7 @@
* Author:
* Colin Cross <ccross@android.com>
*
* Copyright (C) 2010, NVIDIA Corporation
* Copyright (C) 2010,2013, NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
......@@ -23,6 +23,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/syscore_ops.h>
#include "board.h"
#include "iomap.h"
......@@ -43,6 +44,7 @@
#define ICTLR_COP_IEP_CLASS 0x3c
#define FIRST_LEGACY_IRQ 32
#define TEGRA_MAX_NUM_ICTLRS 5
#define SGI_MASK 0xFFFF
......@@ -56,6 +58,15 @@ static void __iomem *ictlr_reg_base[] = {
IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),
};
#ifdef CONFIG_PM_SLEEP
static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS];
static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS];
static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];
static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS];
static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS];
#endif
bool tegra_pending_sgi(void)
{
u32 pending_set;
......@@ -125,6 +136,87 @@ static int tegra_retrigger(struct irq_data *d)
return 1;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_set_wake(struct irq_data *d, unsigned int enable)
{
u32 irq = d->irq;
u32 index, mask;
if (irq < FIRST_LEGACY_IRQ ||
irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32)
return -EINVAL;
index = ((irq - FIRST_LEGACY_IRQ) / 32);
mask = BIT((irq - FIRST_LEGACY_IRQ) % 32);
if (enable)
ictlr_wake_mask[index] |= mask;
else
ictlr_wake_mask[index] &= ~mask;
return 0;
}
static int tegra_legacy_irq_suspend(void)
{
unsigned long flags;
int i;
local_irq_save(flags);
for (i = 0; i < num_ictlrs; i++) {
void __iomem *ictlr = ictlr_reg_base[i];
/* Save interrupt state */
cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER);
cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS);
cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER);
cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
/* Disable COP interrupts */
writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
/* Disable CPU interrupts */
writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
/* Enable the wakeup sources of ictlr */
writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET);
}
local_irq_restore(flags);
return 0;
}
static void tegra_legacy_irq_resume(void)
{
unsigned long flags;
int i;
local_irq_save(flags);
for (i = 0; i < num_ictlrs; i++) {
void __iomem *ictlr = ictlr_reg_base[i];
writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS);
writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
}
local_irq_restore(flags);
}
static struct syscore_ops tegra_legacy_irq_syscore_ops = {
.suspend = tegra_legacy_irq_suspend,
.resume = tegra_legacy_irq_resume,
};
int tegra_legacy_irq_syscore_init(void)
{
register_syscore_ops(&tegra_legacy_irq_syscore_ops);
return 0;
}
#else
#define tegra_set_wake NULL
#endif
void __init tegra_init_irq(void)
{
int i;
......@@ -150,6 +242,8 @@ void __init tegra_init_irq(void)
gic_arch_extn.irq_mask = tegra_mask;
gic_arch_extn.irq_unmask = tegra_unmask;
gic_arch_extn.irq_retrigger = tegra_retrigger;
gic_arch_extn.irq_set_wake = tegra_set_wake;
gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND;
/*
* Check if there is a devicetree present, since the GIC will be
......
......@@ -19,4 +19,10 @@
bool tegra_pending_sgi(void);
#ifdef CONFIG_PM_SLEEP
int tegra_legacy_irq_syscore_init(void);
#else
static inline int tegra_legacy_irq_syscore_init(void) { return 0; }
#endif
#endif
......@@ -26,22 +26,16 @@
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <mach/powergate.h>
#include "fuse.h"
#include "flowctrl.h"
#include "reset.h"
#include "pmc.h"
#include "common.h"
#include "iomap.h"
extern void tegra_secondary_startup(void);
static cpumask_t tegra_cpu_init_mask;
#define EVP_CPU_RESET_VECTOR \
(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
static void __cpuinit tegra_secondary_init(unsigned int cpu)
{
/*
......@@ -54,25 +48,43 @@ static void __cpuinit tegra_secondary_init(unsigned int cpu)
cpumask_set_cpu(cpu, &tegra_cpu_init_mask);
}
static int tegra20_power_up_cpu(unsigned int cpu)
static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
/* Enable the CPU clock. */
tegra_enable_cpu_clock(cpu);
cpu = cpu_logical_map(cpu);
/*
* Force the CPU into reset. The CPU must remain in reset when
* the flow controller state is cleared (which will cause the
* flow controller to stop driving reset if the CPU has been
* power-gated via the flow controller). This will have no
* effect on first boot of the CPU since it should already be
* in reset.
*/
tegra_put_cpu_in_reset(cpu);
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr(cpu, 0);
/*
* Unhalt the CPU. If the flow controller was used to
* power-gate the CPU this will cause the flow controller to
* stop driving reset. The CPU will remain in reset because the
* clock and reset block is now driving reset.
*/
flowctrl_write_cpu_halt(cpu, 0);
tegra_enable_cpu_clock(cpu);
flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */
tegra_cpu_out_of_reset(cpu);
return 0;
}
static int tegra30_power_up_cpu(unsigned int cpu)
static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
int ret, pwrgateid;
int ret;
unsigned long timeout;
pwrgateid = tegra_cpu_powergate_id(cpu);
if (pwrgateid < 0)
return pwrgateid;
cpu = cpu_logical_map(cpu);
tegra_put_cpu_in_reset(cpu);
flowctrl_write_cpu_halt(cpu, 0);
/*
* The power up sequence of cold boot CPU and warm boot CPU
......@@ -85,13 +97,13 @@ static int tegra30_power_up_cpu(unsigned int cpu)
* the IO clamps.
* For cold boot CPU, do not wait. After the cold boot CPU be
* booted, it will run to tegra_secondary_init() and set
* tegra_cpu_init_mask which influences what tegra30_power_up_cpu()
* tegra_cpu_init_mask which influences what tegra30_boot_secondary()
* next time around.
*/
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
timeout = jiffies + msecs_to_jiffies(50);
do {
if (!tegra_powergate_is_powered(pwrgateid))
if (tegra_pmc_cpu_is_powered(cpu))
goto remove_clamps;
udelay(10);
} while (time_before(jiffies, timeout));
......@@ -103,14 +115,14 @@ static int tegra30_power_up_cpu(unsigned int cpu)
* be un-gated by un-toggling the power gate register
* manually.
*/
if (!tegra_powergate_is_powered(pwrgateid)) {
ret = tegra_powergate_power_on(pwrgateid);
if (!tegra_pmc_cpu_is_powered(cpu)) {
ret = tegra_pmc_cpu_power_on(cpu);
if (ret)
return ret;
/* Wait for the power to come up. */
timeout = jiffies + msecs_to_jiffies(100);
while (tegra_powergate_is_powered(pwrgateid)) {
while (tegra_pmc_cpu_is_powered(cpu)) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
udelay(10);
......@@ -123,57 +135,34 @@ static int tegra30_power_up_cpu(unsigned int cpu)
udelay(10);
/* Remove I/O clamps. */
ret = tegra_powergate_remove_clamping(pwrgateid);
udelay(10);
ret = tegra_pmc_cpu_remove_clamping(cpu);
if (ret)
return ret;
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr(cpu, 0);
udelay(10);
flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */
tegra_cpu_out_of_reset(cpu);
return 0;
}
static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
int status;
cpu = cpu_logical_map(cpu);
return tegra_pmc_cpu_power_on(cpu);
}
/*
* Force the CPU into reset. The CPU must remain in reset when the
* flow controller state is cleared (which will cause the flow
* controller to stop driving reset if the CPU has been power-gated
* via the flow controller). This will have no effect on first boot
* of the CPU since it should already be in reset.
*/
tegra_put_cpu_in_reset(cpu);
/*
* Unhalt the CPU. If the flow controller was used to power-gate the
* CPU this will cause the flow controller to stop driving reset.
* The CPU will remain in reset because the clock and reset block
* is now driving reset.
*/
flowctrl_write_cpu_halt(cpu, 0);
switch (tegra_chip_id) {
case TEGRA20:
status = tegra20_power_up_cpu(cpu);
break;
case TEGRA30:
status = tegra30_power_up_cpu(cpu);
break;
default:
status = -EINVAL;
break;
}
if (status)
goto done;
/* Take the CPU out of reset. */
tegra_cpu_out_of_reset(cpu);
done:
return status;
static int __cpuinit tegra_boot_secondary(unsigned int cpu,
struct task_struct *idle)
{
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20)
return tegra20_boot_secondary(cpu, idle);
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30)
return tegra30_boot_secondary(cpu, idle);
if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114)
return tegra114_boot_secondary(cpu, idle);
return -EINVAL;
}
static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
......
......@@ -22,7 +22,7 @@
#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/cpu_pm.h>
#include <linux/clk.h>
#include <linux/suspend.h>
#include <linux/err.h>
#include <linux/clk/tegra.h>
......@@ -37,67 +37,14 @@
#include "reset.h"
#include "flowctrl.h"
#include "fuse.h"
#include "pmc.h"
#include "sleep.h"
#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CTRL 0x0
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc
#include "pmc.h"
#ifdef CONFIG_PM_SLEEP
static unsigned int g_diag_reg;
static DEFINE_SPINLOCK(tegra_lp2_lock);
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
static struct clk *tegra_pclk;
void (*tegra_tear_down_cpu)(void);
void save_cpu_arch_register(void)
{
/* read diagnostic register */
asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc");
return;
}
void restore_cpu_arch_register(void)
{
/* write diagnostic register */
asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc");
return;
}
static void set_power_timers(unsigned long us_on, unsigned long us_off)
{
unsigned long long ticks;
unsigned long long pclk;
unsigned long rate;
static unsigned long tegra_last_pclk;
if (tegra_pclk == NULL) {
tegra_pclk = clk_get_sys(NULL, "pclk");
WARN_ON(IS_ERR(tegra_pclk));
}
rate = clk_get_rate(tegra_pclk);
if (WARN_ON_ONCE(rate <= 0))
pclk = 100000000;
else
pclk = rate;
if ((rate != tegra_last_pclk)) {
ticks = (us_on * pclk) + 999999ull;
do_div(ticks, 1000000);
writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
ticks = (us_off * pclk) + 999999ull;
do_div(ticks, 1000000);
writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
wmb();
}
tegra_last_pclk = pclk;
}
/*
* restore_cpu_complex
*
......@@ -119,8 +66,6 @@ static void restore_cpu_complex(void)
tegra_cpu_clock_resume();
flowctrl_cpu_suspend_exit(cpu);
restore_cpu_arch_register();
}
/*
......@@ -145,8 +90,6 @@ static void suspend_cpu_complex(void)
tegra_cpu_clock_suspend();
flowctrl_cpu_suspend_enter(cpu);
save_cpu_arch_register();
}
void tegra_clear_cpu_in_lp2(int phy_cpu_id)
......@@ -197,16 +140,9 @@ static int tegra_sleep_cpu(unsigned long v2p)
return 0;
}
void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)
void tegra_idle_lp2_last(void)
{
u32 mode;
/* Only the last cpu down does the final suspend steps */
mode = readl(pmc + PMC_CTRL);
mode |= TEGRA_POWER_CPU_PWRREQ_OE;
writel(mode, pmc + PMC_CTRL);
set_power_timers(cpu_on_time, cpu_off_time);
tegra_pmc_pm_set(TEGRA_SUSPEND_LP2);
cpu_cluster_pm_enter();
suspend_cpu_complex();
......@@ -216,4 +152,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)
restore_cpu_complex();
cpu_cluster_pm_exit();
}
enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
enum tegra_suspend_mode mode)
{
/* Tegra114 didn't support any suspending mode yet. */
if (tegra_chip_id == TEGRA114)
return TEGRA_SUSPEND_NONE;
/*
* The Tegra devices only support suspending to LP2 currently.
*/
if (mode > TEGRA_SUSPEND_LP2)
return TEGRA_SUSPEND_LP2;
return mode;
}
static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
[TEGRA_SUSPEND_NONE] = "none",
[TEGRA_SUSPEND_LP2] = "LP2",
[TEGRA_SUSPEND_LP1] = "LP1",
[TEGRA_SUSPEND_LP0] = "LP0",
};
static int __cpuinit tegra_suspend_enter(suspend_state_t state)
{
enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
if (WARN_ON(mode < TEGRA_SUSPEND_NONE ||
mode >= TEGRA_MAX_SUSPEND_MODE))
return -EINVAL;
pr_info("Entering suspend state %s\n", lp_state[mode]);
tegra_pmc_pm_set(mode);
local_fiq_disable();
suspend_cpu_complex();
switch (mode) {
case TEGRA_SUSPEND_LP2:
tegra_set_cpu_in_lp2(0);
break;
default:
break;
}
cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
switch (mode) {
case TEGRA_SUSPEND_LP2:
tegra_clear_cpu_in_lp2(0);
break;
default:
break;
}
restore_cpu_complex();
local_fiq_enable();
return 0;
}
static const struct platform_suspend_ops tegra_suspend_ops = {
.valid = suspend_valid_only_mem,
.enter = tegra_suspend_enter,
};
void __init tegra_init_suspend(void)
{
if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE)
return;
tegra_pmc_suspend_init();
suspend_set_ops(&tegra_suspend_ops);
}
#endif
......@@ -21,6 +21,8 @@
#ifndef _MACH_TEGRA_PM_H_
#define _MACH_TEGRA_PM_H_
#include "pmc.h"
extern unsigned long l2x0_saved_regs_addr;
void save_cpu_arch_register(void);
......@@ -29,7 +31,20 @@ void restore_cpu_arch_register(void);
void tegra_clear_cpu_in_lp2(int phy_cpu_id);
bool tegra_set_cpu_in_lp2(int phy_cpu_id);
void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time);
void tegra_idle_lp2_last(void);
extern void (*tegra_tear_down_cpu)(void);
#ifdef CONFIG_PM_SLEEP
enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
enum tegra_suspend_mode mode);
void tegra_init_suspend(void);
#else
enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
enum tegra_suspend_mode mode)
{
return TEGRA_SUSPEND_NONE;
}
static inline void tegra_init_suspend(void) {}
#endif
#endif /* _MACH_TEGRA_PM_H_ */
/*
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2012,2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
......@@ -16,59 +16,313 @@
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "iomap.h"
#include "fuse.h"
#include "pm.h"
#include "pmc.h"
#include "sleep.h"
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
#define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */
#define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */
#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
#define PMC_PWRGATE_TOGGLE 0x30
#define PMC_PWRGATE_TOGGLE_START (1 << 8)
#define PMC_REMOVE_CLAMPING 0x34
#define PMC_PWRGATE_STATUS 0x38
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc
#define TEGRA_POWERGATE_PCIE 3
#define TEGRA_POWERGATE_VDEC 4
#define TEGRA_POWERGATE_CPU1 9
#define TEGRA_POWERGATE_CPU2 10
#define TEGRA_POWERGATE_CPU3 11
static u8 tegra_cpu_domains[] = {
0xFF, /* not available for CPU0 */
TEGRA_POWERGATE_CPU1,
TEGRA_POWERGATE_CPU2,
TEGRA_POWERGATE_CPU3,
};
static DEFINE_SPINLOCK(tegra_powergate_lock);
static void __iomem *tegra_pmc_base;
static bool tegra_pmc_invert_interrupt;
static struct clk *tegra_pclk;
struct pmc_pm_data {
u32 cpu_good_time; /* CPU power good time in uS */
u32 cpu_off_time; /* CPU power off time in uS */
u32 core_osc_time; /* Core power good osc time in uS */
u32 core_pmu_time; /* Core power good pmu time in uS */
u32 core_off_time; /* Core power off time in uS */
bool corereq_high; /* Core power request active-high */
bool sysclkreq_high; /* System clock request active-high */
bool combined_req; /* Combined pwr req for CPU & Core */
bool cpu_pwr_good_en; /* CPU power good signal is enabled */
u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */
u32 lp0_vec_size; /* The size of LP0 warm boot code */
enum tegra_suspend_mode suspend_mode;
};
static struct pmc_pm_data pmc_pm_data;
static inline u32 tegra_pmc_readl(u32 reg)
{
return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg));
return readl(tegra_pmc_base + reg);
}
static inline void tegra_pmc_writel(u32 val, u32 reg)
{
writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg));
writel(val, tegra_pmc_base + reg);
}
static int tegra_pmc_get_cpu_powerdomain_id(int cpuid)
{
if (cpuid <= 0 || cpuid >= num_possible_cpus())
return -EINVAL;
return tegra_cpu_domains[cpuid];
}
static bool tegra_pmc_powergate_is_powered(int id)
{
return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1;
}
static int tegra_pmc_powergate_set(int id, bool new_state)
{
bool old_state;
unsigned long flags;
spin_lock_irqsave(&tegra_powergate_lock, flags);
old_state = tegra_pmc_powergate_is_powered(id);
WARN_ON(old_state == new_state);
tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE);
spin_unlock_irqrestore(&tegra_powergate_lock, flags);
return 0;
}
static int tegra_pmc_powergate_remove_clamping(int id)
{
u32 mask;
/*
* Tegra has a bug where PCIE and VDE clamping masks are
* swapped relatively to the partition ids.
*/
if (id == TEGRA_POWERGATE_VDEC)
mask = (1 << TEGRA_POWERGATE_PCIE);
else if (id == TEGRA_POWERGATE_PCIE)
mask = (1 << TEGRA_POWERGATE_VDEC);
else
mask = (1 << id);
tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING);
return 0;
}
bool tegra_pmc_cpu_is_powered(int cpuid)
{
int id;
id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
if (id < 0)
return false;
return tegra_pmc_powergate_is_powered(id);
}
#ifdef CONFIG_OF
int tegra_pmc_cpu_power_on(int cpuid)
{
int id;
id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
if (id < 0)
return id;
return tegra_pmc_powergate_set(id, true);
}
int tegra_pmc_cpu_remove_clamping(int cpuid)
{
int id;
id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
if (id < 0)
return id;
return tegra_pmc_powergate_remove_clamping(id);
}
#ifdef CONFIG_PM_SLEEP
static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate)
{
unsigned long long ticks;
unsigned long long pclk;
static unsigned long tegra_last_pclk;
if (WARN_ON_ONCE(rate <= 0))
pclk = 100000000;
else
pclk = rate;
if ((rate != tegra_last_pclk)) {
ticks = (us_on * pclk) + 999999ull;
do_div(ticks, 1000000);
tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER);
ticks = (us_off * pclk) + 999999ull;
do_div(ticks, 1000000);
tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER);
wmb();
}
tegra_last_pclk = pclk;
}
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
{
return pmc_pm_data.suspend_mode;
}
void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
{
u32 reg;
unsigned long rate = 0;
reg = tegra_pmc_readl(PMC_CTRL);
reg |= TEGRA_POWER_CPU_PWRREQ_OE;
reg &= ~TEGRA_POWER_EFFECT_LP0;
switch (mode) {
case TEGRA_SUSPEND_LP2:
rate = clk_get_rate(tegra_pclk);
break;
default:
break;
}
set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time,
rate);
tegra_pmc_writel(reg, PMC_CTRL);
}
void tegra_pmc_suspend_init(void)
{
u32 reg;
/* Always enable CPU power request */
reg = tegra_pmc_readl(PMC_CTRL);
reg |= TEGRA_POWER_CPU_PWRREQ_OE;
tegra_pmc_writel(reg, PMC_CTRL);
}
#endif
static const struct of_device_id matches[] __initconst = {
{ .compatible = "nvidia,tegra114-pmc" },
{ .compatible = "nvidia,tegra30-pmc" },
{ .compatible = "nvidia,tegra20-pmc" },
{ }
};
#endif
void __init tegra_pmc_init(void)
static void tegra_pmc_parse_dt(void)
{
/*
* For now, Harmony is the only board that uses the PMC, and it wants
* the signal inverted. Seaboard would too if it used the PMC.
* Hopefully by the time other boards want to use the PMC, everything
* will be device-tree, or they also want it inverted.
*/
bool invert_interrupt = true;
u32 val;
struct device_node *np;
u32 prop;
enum tegra_suspend_mode suspend_mode;
u32 core_good_time[2] = {0, 0};
u32 lp0_vec[2] = {0, 0};
#ifdef CONFIG_OF
if (of_have_populated_dt()) {
struct device_node *np;
np = of_find_matching_node(NULL, matches);
BUG_ON(!np);
invert_interrupt = false;
tegra_pmc_base = of_iomap(np, 0);
np = of_find_matching_node(NULL, matches);
if (np) {
if (of_find_property(np, "nvidia,invert-interrupt",
NULL))
invert_interrupt = true;
tegra_pmc_invert_interrupt = of_property_read_bool(np,
"nvidia,invert-interrupt");
tegra_pclk = of_clk_get_by_name(np, "pclk");
WARN_ON(IS_ERR(tegra_pclk));
/* Grabbing the power management configurations */
if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) {
suspend_mode = TEGRA_SUSPEND_NONE;
} else {
switch (prop) {
case 0:
suspend_mode = TEGRA_SUSPEND_LP0;
break;
case 1:
suspend_mode = TEGRA_SUSPEND_LP1;
break;
case 2:
suspend_mode = TEGRA_SUSPEND_LP2;
break;
default:
suspend_mode = TEGRA_SUSPEND_NONE;
break;
}
}
#endif
suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode);
if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop))
suspend_mode = TEGRA_SUSPEND_NONE;
pmc_pm_data.cpu_good_time = prop;
if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop))
suspend_mode = TEGRA_SUSPEND_NONE;
pmc_pm_data.cpu_off_time = prop;
if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time",
core_good_time, ARRAY_SIZE(core_good_time)))
suspend_mode = TEGRA_SUSPEND_NONE;
pmc_pm_data.core_osc_time = core_good_time[0];
pmc_pm_data.core_pmu_time = core_good_time[1];
if (of_property_read_u32(np, "nvidia,core-pwr-off-time",
&prop))
suspend_mode = TEGRA_SUSPEND_NONE;
pmc_pm_data.core_off_time = prop;
pmc_pm_data.corereq_high = of_property_read_bool(np,
"nvidia,core-power-req-active-high");
pmc_pm_data.sysclkreq_high = of_property_read_bool(np,
"nvidia,sys-clock-req-active-high");
pmc_pm_data.combined_req = of_property_read_bool(np,
"nvidia,combined-power-req");
pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np,
"nvidia,cpu-pwr-good-en");
if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec,
ARRAY_SIZE(lp0_vec)))
if (suspend_mode == TEGRA_SUSPEND_LP0)
suspend_mode = TEGRA_SUSPEND_LP1;
pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0];
pmc_pm_data.lp0_vec_size = lp0_vec[1];
pmc_pm_data.suspend_mode = suspend_mode;
}
void __init tegra_pmc_init(void)
{
u32 val;
tegra_pmc_parse_dt();
val = tegra_pmc_readl(PMC_CTRL);
if (invert_interrupt)
if (tegra_pmc_invert_interrupt)
val |= PMC_CTRL_INTR_LOW;
else
val &= ~PMC_CTRL_INTR_LOW;
......
......@@ -18,6 +18,24 @@
#ifndef __MACH_TEGRA_PMC_H
#define __MACH_TEGRA_PMC_H
enum tegra_suspend_mode {
TEGRA_SUSPEND_NONE = 0,
TEGRA_SUSPEND_LP2, /* CPU voltage off */
TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */
TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */
TEGRA_MAX_SUSPEND_MODE,
};
#ifdef CONFIG_PM_SLEEP
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
void tegra_pmc_pm_set(enum tegra_suspend_mode mode);
void tegra_pmc_suspend_init(void);
#endif
bool tegra_pmc_cpu_is_powered(int cpuid);
int tegra_pmc_cpu_power_on(int cpuid);
int tegra_pmc_cpu_remove_clamping(int cpuid);
void tegra_pmc_init(void);
#endif
......@@ -41,9 +41,6 @@
*/
ENTRY(tegra_resume)
bl v7_invalidate_l1
/* Enable coresight */
mov32 r0, 0xC5ACCE55
mcr p14, 0, r0, c7, c12, 6
cpu_id r0
cmp r0, #0 @ CPU0?
......@@ -99,6 +96,8 @@ ENTRY(__tegra_cpu_reset_handler_start)
*
* Register usage within the reset handler:
*
* Others: scratch
* R6 = SoC ID << 8
* R7 = CPU present (to the OS) mask
* R8 = CPU in LP1 state mask
* R9 = CPU in LP2 state mask
......@@ -114,6 +113,40 @@ ENTRY(__tegra_cpu_reset_handler_start)
ENTRY(__tegra_cpu_reset_handler)
cpsid aif, 0x13 @ SVC mode, interrupts disabled
mov32 r6, TEGRA_APB_MISC_BASE
ldr r6, [r6, #APB_MISC_GP_HIDREV]
and r6, r6, #0xff00
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
t20_check:
cmp r6, #(0x20 << 8)
bne after_t20_check
t20_errata:
# Tegra20 is a Cortex-A9 r1p1
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 << 14 @ erratum 716044
mcr p15, 0, r0, c1, c0, 0 @ write system control register
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 4 @ erratum 742230
orr r0, r0, #1 << 11 @ erratum 751472
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
b after_errata
after_t20_check:
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
t30_check:
cmp r6, #(0x30 << 8)
bne after_t30_check
t30_errata:
# Tegra30 is a Cortex-A9 r2p9
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 6 @ erratum 743622
orr r0, r0, #1 << 11 @ erratum 751472
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
b after_errata
after_t30_check:
#endif
after_errata:
mrc p15, 0, r10, c0, c0, 5 @ MPIDR
and r10, r10, #0x3 @ R10 = CPU number
mov r11, #1
......@@ -129,16 +162,13 @@ ENTRY(__tegra_cpu_reset_handler)
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
/* Are we on Tegra20? */
mov32 r6, TEGRA_APB_MISC_BASE
ldr r0, [r6, #APB_MISC_GP_HIDREV]
and r0, r0, #0xff00
cmp r0, #(0x20 << 8)
cmp r6, #(0x20 << 8)
bne 1f
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
mov32 r6, TEGRA_PMC_BASE
mov32 r5, TEGRA_PMC_BASE
mov r0, #0
cmp r10, #0
strne r0, [r6, #PMC_SCRATCH41]
strne r0, [r5, #PMC_SCRATCH41]
1:
#endif
......
/*
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
* Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
......@@ -124,11 +124,11 @@ int tegra_sleep_cpu_finish(unsigned long);
void tegra_disable_clean_inv_dcache(void);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);
void tegra30_hotplug_init(void);
void tegra20_hotplug_shutdown(void);
void tegra30_hotplug_shutdown(void);
void tegra_hotplug_init(void);
#else
static inline void tegra20_hotplug_init(void) {}
static inline void tegra30_hotplug_init(void) {}
static inline void tegra_hotplug_init(void) {}
#endif
void tegra20_cpu_shutdown(int cpu);
......
/*
* nVidia Tegra device tree board support
* NVIDIA Tegra SoC device tree board support
*
* Copyright (C) 2011, 2013, NVIDIA Corporation
* Copyright (C) 2010 Secret Lab Technologies, Ltd.
* Copyright (C) 2010 Google, Inc.
*
......@@ -32,7 +33,10 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/i2c-tegra.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/usb/tegra_usb_phy.h>
#include <linux/clk/tegra.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
......@@ -41,6 +45,7 @@
#include "board.h"
#include "common.h"
#include "fuse.h"
#include "iomap.h"
static struct tegra_ehci_platform_data tegra_ehci1_pdata = {
......@@ -79,12 +84,38 @@ static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
static void __init tegra_dt_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device *parent = NULL;
tegra_clocks_apply_init_table();
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
goto out;
soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra");
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision);
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", tegra_chip_id);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree(soc_dev_attr->family);
kfree(soc_dev_attr->revision);
kfree(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
goto out;
}
parent = soc_device_to_device(soc_dev);
/*
* Finished with the static registrations now; fill in the missing
* devices
*/
out:
of_platform_populate(NULL, of_default_bus_match_table,
tegra20_auxdata_lookup, NULL);
tegra20_auxdata_lookup, parent);
}
static void __init trimslice_init(void)
......@@ -111,7 +142,8 @@ static void __init harmony_init(void)
static void __init paz00_init(void)
{
tegra_paz00_wifikill_init();
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
tegra_paz00_wifikill_init();
}
static struct {
......@@ -137,19 +169,21 @@ static void __init tegra_dt_init_late(void)
}
}
static const char *tegra20_dt_board_compat[] = {
static const char * const tegra_dt_board_compat[] = {
"nvidia,tegra114",
"nvidia,tegra30",
"nvidia,tegra20",
NULL
};
DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)")
DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)")
.map_io = tegra_map_common_io,
.smp = smp_ops(tegra_smp_ops),
.init_early = tegra20_init_early,
.init_early = tegra_init_early,
.init_irq = tegra_dt_init_irq,
.init_time = clocksource_of_init,
.init_machine = tegra_dt_init,
.init_late = tegra_dt_init_late,
.restart = tegra_assert_system_reset,
.dt_compat = tegra20_dt_board_compat,
.dt_compat = tegra_dt_board_compat,
MACHINE_END
/*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/bug.h>
#include "fuse.h"
#define CORE_PROCESS_CORNERS_NUM 2
#define CPU_PROCESS_CORNERS_NUM 2
enum {
THRESHOLD_INDEX_0,
THRESHOLD_INDEX_1,
THRESHOLD_INDEX_COUNT,
};
static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = {
{1123, UINT_MAX},
{0, UINT_MAX},
};
static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = {
{1695, UINT_MAX},
{0, UINT_MAX},
};
static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold)
{
u32 tmp;
switch (sku) {
case 0x00:
case 0x10:
case 0x05:
case 0x06:
tegra_cpu_speedo_id = 1;
tegra_soc_speedo_id = 0;
*threshold = THRESHOLD_INDEX_0;
break;
case 0x03:
case 0x04:
tegra_cpu_speedo_id = 2;
tegra_soc_speedo_id = 1;
*threshold = THRESHOLD_INDEX_1;
break;
default:
pr_err("Tegra114 Unknown SKU %d\n", sku);
tegra_cpu_speedo_id = 0;
tegra_soc_speedo_id = 0;
*threshold = THRESHOLD_INDEX_0;
break;
}
if (rev == TEGRA_REVISION_A01) {
tmp = tegra_fuse_readl(0x270) << 1;
tmp |= tegra_fuse_readl(0x26c);
if (!tmp)
tegra_cpu_speedo_id = 0;
}
}
void tegra114_init_speedo_data(void)
{
u32 cpu_speedo_val;
u32 core_speedo_val;
int threshold;
int i;
BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
THRESHOLD_INDEX_COUNT);
BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) !=
THRESHOLD_INDEX_COUNT);
rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold);
cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024;
core_speedo_val = tegra_fuse_readl(0x134);
for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++)
if (cpu_speedo_val < cpu_process_speedos[threshold][i])
break;
tegra_cpu_process_id = i;
for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++)
if (core_speedo_val < core_process_speedos[threshold][i])
break;
tegra_core_process_id = i;
}
......@@ -63,6 +63,14 @@ config CLK_TWL6040
McPDM. McPDM module is using the external bit clock on the McPDM bus
as functional clock.
config COMMON_CLK_AXI_CLKGEN
tristate "AXI clkgen driver"
depends on ARCH_ZYNQ || MICROBLAZE
help
---help---
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
FPGAs. It is commonly used in Analog Devices' reference designs.
endmenu
source "drivers/clk/mvebu/Kconfig"
......@@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
# SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
......@@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
......@@ -31,6 +33,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_X86) += x86/
# Chip specific
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
/*
* AXI clkgen driver
*
* Copyright 2012-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*
*/
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/err.h>
#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
#define AXI_CLKGEN_REG_CLK_OUT1 0x08
#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
#define AXI_CLKGEN_REG_CLK_DIV 0x10
#define AXI_CLKGEN_REG_CLK_FB1 0x14
#define AXI_CLKGEN_REG_CLK_FB2 0x18
#define AXI_CLKGEN_REG_LOCK1 0x1c
#define AXI_CLKGEN_REG_LOCK2 0x20
#define AXI_CLKGEN_REG_LOCK3 0x24
#define AXI_CLKGEN_REG_FILTER1 0x28
#define AXI_CLKGEN_REG_FILTER2 0x2c
struct axi_clkgen {
void __iomem *base;
struct clk_hw clk_hw;
};
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
{
switch (m) {
case 0:
return 0x01001990;
case 1:
return 0x01001190;
case 2:
return 0x01009890;
case 3:
return 0x01001890;
case 4:
return 0x01008890;
case 5 ... 8:
return 0x01009090;
case 9 ... 11:
return 0x01000890;
case 12:
return 0x08009090;
case 13 ... 22:
return 0x01001090;
case 23 ... 36:
return 0x01008090;
case 37 ... 46:
return 0x08001090;
default:
return 0x08008090;
}
}
static const uint32_t axi_clkgen_lock_table[] = {
0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
};
static uint32_t axi_clkgen_lookup_lock(unsigned int m)
{
if (m < ARRAY_SIZE(axi_clkgen_lock_table))
return axi_clkgen_lock_table[m];
return 0x1f1f00fa;
}
static const unsigned int fpfd_min = 10000;
static const unsigned int fpfd_max = 300000;
static const unsigned int fvco_min = 600000;
static const unsigned int fvco_max = 1200000;
static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
{
unsigned long d, d_min, d_max, _d_min, _d_max;
unsigned long m, m_min, m_max;
unsigned long f, dout, best_f, fvco;
fin /= 1000;
fout /= 1000;
best_f = ULONG_MAX;
*best_d = 0;
*best_m = 0;
*best_dout = 0;
d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
d_max = min_t(unsigned long, fin / fpfd_min, 80);
m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
for (m = m_min; m <= m_max; m++) {
_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
_d_max = min(d_max, fin * m / fvco_min);
for (d = _d_min; d <= _d_max; d++) {
fvco = fin * m / d;
dout = DIV_ROUND_CLOSEST(fvco, fout);
dout = clamp_t(unsigned long, dout, 1, 128);
f = fvco / dout;
if (abs(f - fout) < abs(best_f - fout)) {
best_f = f;
*best_d = d;
*best_m = m;
*best_dout = dout;
if (best_f == fout)
return;
}
}
}
}
static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
unsigned int *high, unsigned int *edge, unsigned int *nocount)
{
if (divider == 1)
*nocount = 1;
else
*nocount = 0;
*high = divider / 2;
*edge = divider % 2;
*low = divider - *high;
}
static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int val)
{
writel(val, axi_clkgen->base + reg);
}
static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int *val)
{
*val = readl(axi_clkgen->base + reg);
}
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct axi_clkgen, clk_hw);
}
static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
unsigned long rate, unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
unsigned int nocount;
unsigned int high;
unsigned int edge;
unsigned int low;
uint32_t filter;
uint32_t lock;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
filter = axi_clkgen_lookup_filter(m - 1);
lock = axi_clkgen_lookup_lock(m - 1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
(edge << 7) | (nocount << 6));
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
(edge << 13) | (nocount << 12) | (high << 6) | low);
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
(edge << 7) | (nocount << 6));
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
(((lock >> 16) & 0x1f) << 10) | 0x1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
(((lock >> 24) & 0x1f) << 10) | 0x3e9);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
return 0;
}
static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned int d, m, dout;
axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
return *parent_rate / d * m / dout;
}
static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
unsigned int reg;
unsigned long long tmp;
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
if (d == 0 || dout == 0)
return 0;
tmp = (unsigned long long)(parent_rate / d) * m;
do_div(tmp, dout);
if (tmp > ULONG_MAX)
return ULONG_MAX;
return tmp;
}
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate,
};
static int axi_clkgen_probe(struct platform_device *pdev)
{
struct axi_clkgen *axi_clkgen;
struct clk_init_data init;
const char *parent_name;
const char *clk_name;
struct resource *mem;
struct clk *clk;
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
if (!axi_clkgen)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(axi_clkgen->base))
return PTR_ERR(axi_clkgen->base);
parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
if (!parent_name)
return -EINVAL;
clk_name = pdev->dev.of_node->name;
of_property_read_string(pdev->dev.of_node, "clock-output-names",
&clk_name);
init.name = clk_name;
init.ops = &axi_clkgen_ops;
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
axi_clkgen->clk_hw.init = &init;
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
clk);
}
static int axi_clkgen_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
return 0;
}
static const struct of_device_id axi_clkgen_ids[] = {
{ .compatible = "adi,axi-clkgen-1.00.a" },
{ },
};
MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
.owner = THIS_MODULE,
.of_match_table = axi_clkgen_ids,
},
.probe = axi_clkgen_probe,
.remove = axi_clkgen_remove,
};
module_platform_driver(axi_clkgen_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
/*
* Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/slab.h>
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
static u8 clk_composite_get_parent(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->get_parent(mux_hw);
}
static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->set_parent(mux_hw, index);
}
static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->recalc_rate(div_hw, parent_rate);
}
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->round_rate(div_hw, rate, prate);
}
static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->set_rate(div_hw, rate, parent_rate);
}
static int clk_composite_is_enabled(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->is_enabled(gate_hw);
}
static int clk_composite_enable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->enable(gate_hw);
}
static void clk_composite_disable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
gate_ops->disable(gate_hw);
}
struct clk *clk_register_composite(struct device *dev, const char *name,
const char **parent_names, int num_parents,
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
struct clk_hw *div_hw, const struct clk_ops *div_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
struct clk_composite *composite;
struct clk_ops *clk_composite_ops;
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
if (!composite) {
pr_err("%s: could not allocate composite clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = parent_names;
init.num_parents = num_parents;
clk_composite_ops = &composite->ops;
if (mux_hw && mux_ops) {
if (!mux_ops->get_parent || !mux_ops->set_parent) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->mux_hw = mux_hw;
composite->mux_ops = mux_ops;
clk_composite_ops->get_parent = clk_composite_get_parent;
clk_composite_ops->set_parent = clk_composite_set_parent;
}
if (div_hw && div_ops) {
if (!div_ops->recalc_rate || !div_ops->round_rate ||
!div_ops->set_rate) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->div_hw = div_hw;
composite->div_ops = div_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
clk_composite_ops->round_rate = clk_composite_round_rate;
clk_composite_ops->set_rate = clk_composite_set_rate;
}
if (gate_hw && gate_ops) {
if (!gate_ops->is_enabled || !gate_ops->enable ||
!gate_ops->disable) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->gate_hw = gate_hw;
composite->gate_ops = gate_ops;
clk_composite_ops->is_enabled = clk_composite_is_enabled;
clk_composite_ops->enable = clk_composite_enable;
clk_composite_ops->disable = clk_composite_disable;
}
init.ops = clk_composite_ops;
composite->hw.init = &init;
clk = clk_register(dev, &composite->hw);
if (IS_ERR(clk))
goto err;
if (composite->mux_hw)
composite->mux_hw->clk = clk;
if (composite->div_hw)
composite->div_hw->clk = clk;
if (composite->gate_hw)
composite->gate_hw->clk = clk;
return clk;
err:
kfree(composite);
return clk;
}
......@@ -32,6 +32,7 @@
static u8 clk_mux_get_parent(struct clk_hw *hw)
{
struct clk_mux *mux = to_clk_mux(hw);
int num_parents = __clk_get_num_parents(hw->clk);
u32 val;
/*
......@@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
* val = 0x4 really means "bit 2, index starts at bit 0"
*/
val = readl(mux->reg) >> mux->shift;
val &= (1 << mux->width) - 1;
val &= mux->mask;
if (mux->table) {
int i;
for (i = 0; i < num_parents; i++)
if (mux->table[i] == val)
return i;
return -EINVAL;
}
if (val && (mux->flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
......@@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
if (val && (mux->flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= __clk_get_num_parents(hw->clk))
if (val >= num_parents)
return -EINVAL;
return val;
......@@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
u32 val;
unsigned long flags = 0;
if (mux->flags & CLK_MUX_INDEX_BIT)
index = (1 << ffs(index));
if (mux->table)
index = mux->table[index];
if (mux->flags & CLK_MUX_INDEX_ONE)
index++;
else {
if (mux->flags & CLK_MUX_INDEX_BIT)
index = (1 << ffs(index));
if (mux->flags & CLK_MUX_INDEX_ONE)
index++;
}
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
val = readl(mux->reg);
val &= ~(((1 << mux->width) - 1) << mux->shift);
val &= ~(mux->mask << mux->shift);
val |= index << mux->shift;
writel(val, mux->reg);
......@@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = {
};
EXPORT_SYMBOL_GPL(clk_mux_ops);
struct clk *clk_register_mux(struct device *dev, const char *name,
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{
struct clk_mux *mux;
struct clk *clk;
......@@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
/* struct clk_mux assignments */
mux->reg = reg;
mux->shift = shift;
mux->width = width;
mux->mask = mask;
mux->flags = clk_mux_flags;
mux->lock = lock;
mux->table = table;
mux->hw.init = &init;
clk = clk_register(dev, &mux->hw);
......@@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
return clk;
}
struct clk *clk_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
{
u32 mask = BIT(width) - 1;
return clk_register_mux_table(dev, name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags,
NULL, lock);
}
......@@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void)
for (i = pll1; i < maxclk; i++) {
prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
BUG_ON(!prima2_clks[i]);
BUG_ON(IS_ERR(prima2_clks[i]));
}
clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
clk_register_clkdev(prima2_clks[io], NULL, "io");
......
......@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clk/zynq.h>
static void __iomem *slcr_base;
......
This diff is collapsed.
......@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include "clk.h"
DEFINE_SPINLOCK(mxs_lock);
......
......@@ -960,47 +960,47 @@ void __init spear1340_clk_init(void)
SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);
clk_register_clkdev(clk, NULL, "d0100000.spdif-in");
clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0,
clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,
SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "acp_clk");
clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0,
clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "e2800000.gpio");
clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0,
clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,
0, &_lock);
clk_register_clkdev(clk, NULL, "video_dec");
clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0,
clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,
0, &_lock);
clk_register_clkdev(clk, NULL, "video_enc");
clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0,
clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "spear_vip");
clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0,
clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0200000.cam0");
clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0,
clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0300000.cam1");
clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0,
clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0400000.cam2");
clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0,
clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0500000.cam3");
......
#
# Makefile for sunxi specific clk
#
obj-y += clk-sunxi.o clk-factors.o
/*
* Copyright (C) 2013 Emilio López <emilio@elopez.com.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Adjustable factor-based clock implementation
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/delay.h>
#include "clk-factors.h"
/*
* DOC: basic adjustable factor-based clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable.
* clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
* parent - fixed parent. No clk_set_parent support
*/
struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
struct clk_factors_config *config;
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
spinlock_t *lock;
};
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
#define FACTOR_SET(bit, len, reg, val) \
(((reg) & CLRMASK(len, bit)) | (val << (bit)))
static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u8 n = 1, k = 0, p = 0, m = 0;
u32 reg;
unsigned long rate;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
/* Fetch the register value */
reg = readl(factors->reg);
/* Get each individual factor if applicable */
if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
n = FACTOR_GET(config->nshift, config->nwidth, reg);
if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
k = FACTOR_GET(config->kshift, config->kwidth, reg);
if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
m = FACTOR_GET(config->mshift, config->mwidth, reg);
if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
p = FACTOR_GET(config->pshift, config->pwidth, reg);
/* Calculate the rate */
rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
return rate;
}
static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_factors *factors = to_clk_factors(hw);
factors->get_factors((u32 *)&rate, (u32)*parent_rate,
NULL, NULL, NULL, NULL);
return rate;
}
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
u8 n, k, m, p;
u32 reg;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
unsigned long flags = 0;
factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
if (factors->lock)
spin_lock_irqsave(factors->lock, flags);
/* Fetch the register value */
reg = readl(factors->reg);
/* Set up the new factors - macros do not do anything if width is 0 */
reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
/* Apply them now */
writel(reg, factors->reg);
/* delay 500us so pll stabilizes */
__delay((rate >> 20) * 500 / 2);
if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);
return 0;
}
static const struct clk_ops clk_factors_ops = {
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
};
/**
* clk_register_factors - register a factors clock with
* the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust factors
* @config: shift and width of factors n, k, m and p
* @get_factors: function to calculate the factors for a given frequency
* @lock: shared register lock for this clock
*/
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors)(u32 *rate, u32 parent,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock)
{
struct clk_factors *factors;
struct clk *clk;
struct clk_init_data init;
/* allocate the factors */
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors) {
pr_err("%s: could not allocate factors clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_factors_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_factors assignments */
factors->reg = reg;
factors->config = config;
factors->lock = lock;
factors->hw.init = &init;
factors->get_factors = get_factors;
/* register the clock */
clk = clk_register(dev, &factors->hw);
if (IS_ERR(clk))
kfree(factors);
return clk;
}
#ifndef __MACH_SUNXI_CLK_FACTORS_H
#define __MACH_SUNXI_CLK_FACTORS_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
struct clk_factors_config {
u8 nshift;
u8 nwidth;
u8 kshift;
u8 kwidth;
u8 mshift;
u8 mwidth;
u8 pshift;
u8 pwidth;
};
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors) (u32 *rate, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock);
#endif
/*
* Copyright 2013 Emilio López
*
* Emilio López <emilio@elopez.com.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/sunxi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock);
/**
* sunxi_osc_clk_setup() - Setup function for gatable oscillator
*/
#define SUNXI_OSC24M_GATE 0
static void __init sunxi_osc_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent;
void *reg;
reg = of_iomap(node, 0);
parent = of_clk_get_parent_name(node, 0);
clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
reg, SUNXI_OSC24M_GATE, 0, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
* PLL1 rate is calculated as follows
* rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
* parent_rate is always 24Mhz
*/
static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 div;
/* Normalize value to a 6M multiple */
div = *freq / 6000000;
*freq = 6000000 * div;
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
/* m is always zero for pll1 */
*m = 0;
/* k is 1 only on these cases */
if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
*k = 1;
else
*k = 0;
/* p will be 3 for divs under 10 */
if (div < 10)
*p = 3;
/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
else if (div < 20 || (div < 32 && (div & 1)))
*p = 2;
/* p will be 1 for even divs under 32, divs under 40 and odd pairs
* of divs between 40-62 */
else if (div < 40 || (div < 64 && (div & 2)))
*p = 1;
/* any other entries have p = 0 */
else
*p = 0;
/* calculate a suitable n based on k and p */
div <<= *p;
div /= (*k + 1);
*n = div / 4;
}
/**
* sunxi_get_apb1_factors() - calculates m, p factors for APB1
* APB1 rate is calculated as follows
* rate = (parent_rate >> p) / (m + 1);
*/
static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 calcm, calcp;
if (parent_rate < *freq)
*freq = parent_rate;
parent_rate = (parent_rate + (*freq - 1)) / *freq;
/* Invalid rate! */
if (parent_rate > 32)
return;
if (parent_rate <= 4)
calcp = 0;
else if (parent_rate <= 8)
calcp = 1;
else if (parent_rate <= 16)
calcp = 2;
else
calcp = 3;
calcm = (parent_rate >> calcp) - 1;
*freq = (parent_rate >> calcp) / (calcm + 1);
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
*m = calcm;
*p = calcp;
}
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
struct factors_data {
struct clk_factors_config *table;
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
};
static struct clk_factors_config pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
.kwidth = 2,
.mshift = 0,
.mwidth = 2,
.pshift = 16,
.pwidth = 2,
};
static struct clk_factors_config apb1_config = {
.mshift = 0,
.mwidth = 5,
.pshift = 16,
.pwidth = 2,
};
static const __initconst struct factors_data pll1_data = {
.table = &pll1_config,
.getter = sunxi_get_pll1_factors,
};
static const __initconst struct factors_data apb1_data = {
.table = &apb1_config,
.getter = sunxi_get_apb1_factors,
};
static void __init sunxi_factors_clk_setup(struct device_node *node,
struct factors_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent;
void *reg;
reg = of_iomap(node, 0);
parent = of_clk_get_parent_name(node, 0);
clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
reg, data->table, data->getter, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_mux_clk_setup() - Setup function for muxes
*/
#define SUNXI_MUX_GATE_WIDTH 2
struct mux_data {
u8 shift;
};
static const __initconst struct mux_data cpu_data = {
.shift = 16,
};
static const __initconst struct mux_data apb1_mux_data = {
.shift = 24,
};
static void __init sunxi_mux_clk_setup(struct device_node *node,
struct mux_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
void *reg;
int i = 0;
reg = of_iomap(node, 0);
while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_divider_clk_setup() - Setup function for simple divider clocks
*/
#define SUNXI_DIVISOR_WIDTH 2
struct div_data {
u8 shift;
u8 pow;
};
static const __initconst struct div_data axi_data = {
.shift = 0,
.pow = 0,
};
static const __initconst struct div_data ahb_data = {
.shift = 4,
.pow = 1,
};
static const __initconst struct div_data apb0_data = {
.shift = 8,
.pow = 1,
};
static void __init sunxi_divider_clk_setup(struct device_node *node,
struct div_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *clk_parent;
void *reg;
reg = of_iomap(node, 0);
clk_parent = of_clk_get_parent_name(node, 0);
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
reg, data->shift, SUNXI_DIVISOR_WIDTH,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
&clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/* Matches for of_clk_init */
static const __initconst struct of_device_id clk_match[] = {
{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
{.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
{}
};
/* Matches for factors clocks */
static const __initconst struct of_device_id clk_factors_match[] = {
{.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,},
{.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,},
{}
};
/* Matches for divider clocks */
static const __initconst struct of_device_id clk_div_match[] = {
{.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,},
{.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,},
{.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,},
{}
};
/* Matches for mux clocks */
static const __initconst struct of_device_id clk_mux_match[] = {
{.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,},
{.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,},
{}
};
static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
void *function)
{
struct device_node *np;
const struct div_data *data;
const struct of_device_id *match;
void (*setup_function)(struct device_node *, const void *) = function;
for_each_matching_node(np, clk_match) {
match = of_match_node(clk_match, np);
data = match->data;
setup_function(np, data);
}
}
void __init sunxi_init_clocks(void)
{
/* Register all the simple sunxi clocks on DT */
of_clk_init(clk_match);
/* Register factor clocks */
of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
/* Register divider clocks */
of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
/* Register mux clocks */
of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
}
......@@ -9,3 +9,4 @@ obj-y += clk-super.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
......@@ -41,7 +41,9 @@ static DEFINE_SPINLOCK(periph_ref_lock);
#define write_rst_clr(val, gate) \
writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32))
#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))
#define LVL2_CLK_GATE_OVRE 0x554
/* Peripheral gate clock ops */
static int clk_periph_is_enabled(struct clk_hw *hw)
......@@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw)
}
}
if (gate->flags & TEGRA_PERIPH_WAR_1005168) {
writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
udelay(1);
writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
}
spin_unlock_irqrestore(&periph_ref_lock, flags);
return 0;
......
......@@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
......@@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c)
tegra_periph_reset(gate, 0);
}
EXPORT_SYMBOL(tegra_periph_reset_deassert);
void tegra_periph_reset_assert(struct clk *c)
{
......@@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c)
tegra_periph_reset(gate, 1);
}
EXPORT_SYMBOL(tegra_periph_reset_assert);
const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
......@@ -170,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = {
static struct clk *_tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph,
void __iomem *clk_base, u32 offset, bool div)
void __iomem *clk_base, u32 offset, bool div,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
init.name = name;
init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
init.flags = div ? 0 : CLK_SET_RATE_PARENT;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
......@@ -202,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name,
struct clk *tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset)
u32 offset, unsigned long flags)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, true);
periph, clk_base, offset, true, flags);
}
struct clk *tegra_clk_register_periph_nodiv(const char *name,
......@@ -214,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
u32 offset)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, false);
periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -22,7 +22,8 @@
#include "clk.h"
/* Global data of Tegra CPU CAR ops */
struct tegra_cpu_car_ops *tegra_cpu_car_ops;
static struct tegra_cpu_car_ops dummy_car_ops;
struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
struct clk *clks[], int clk_max)
......@@ -76,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
static const struct of_device_id tegra_dt_clk_match[] = {
{ .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init },
{ .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init },
{ .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init },
{ }
};
......@@ -83,3 +85,13 @@ void __init tegra_clocks_init(void)
{
of_clk_init(tegra_dt_clk_match);
}
tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
void __init tegra_clocks_apply_init_table(void)
{
if (!tegra_clk_apply_init_table)
return;
tegra_clk_apply_init_table();
}
/*
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
......@@ -116,6 +116,17 @@ struct tegra_clk_pll_freq_table {
u8 cpcon;
};
/**
* struct pdiv_map - map post divider to hw value
*
* @pdiv: post divider
* @hw_val: value to be written to the PLL hw
*/
struct pdiv_map {
u8 pdiv;
u8 hw_val;
};
/**
* struct clk_pll_params - PLL parameters
*
......@@ -143,9 +154,18 @@ struct tegra_clk_pll_params {
u32 base_reg;
u32 misc_reg;
u32 lock_reg;
u32 lock_bit_idx;
u32 lock_mask;
u32 lock_enable_bit_idx;
u32 iddq_reg;
u32 iddq_bit_idx;
u32 aux_reg;
u32 dyn_ramp_reg;
u32 ext_misc_reg[3];
int stepa_shift;
int stepb_shift;
int lock_delay;
int max_p;
struct pdiv_map *pdiv_tohw;
};
/**
......@@ -182,12 +202,16 @@ struct tegra_clk_pll_params {
* TEGRA_PLL_FIXED - We are not supposed to change output frequency
* of some plls.
* TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling.
* TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the
* base register.
* TEGRA_PLL_BYPASS - PLL has bypass bit
* TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring
*/
struct tegra_clk_pll {
struct clk_hw hw;
void __iomem *clk_base;
void __iomem *pmc;
u8 flags;
u32 flags;
unsigned long fixed_rate;
spinlock_t *lock;
u8 divn_shift;
......@@ -210,20 +234,64 @@ struct tegra_clk_pll {
#define TEGRA_PLLM BIT(5)
#define TEGRA_PLL_FIXED BIT(6)
#define TEGRA_PLLE_CONFIGURE BIT(7)
#define TEGRA_PLL_LOCK_MISC BIT(8)
#define TEGRA_PLL_BYPASS BIT(9)
#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10)
extern const struct clk_ops tegra_clk_pll_ops;
extern const struct clk_ops tegra_clk_plle_ops;
struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params, u8 pll_flags,
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params, u8 pll_flags,
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock, unsigned long parent_rate);
struct clk *tegra_clk_register_plle_tegra114(const char *name,
const char *parent_name,
void __iomem *clk_base, unsigned long flags,
unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
/**
* struct tegra_clk_pll_out - PLL divider down clock
*
......@@ -290,6 +358,7 @@ struct tegra_clk_periph_regs {
* TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the
* bus to flush the write operation in apb bus. This flag indicates
* that this peripheral is in apb bus.
* TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug
*/
struct tegra_clk_periph_gate {
u32 magic;
......@@ -309,6 +378,7 @@ struct tegra_clk_periph_gate {
#define TEGRA_PERIPH_NO_RESET BIT(0)
#define TEGRA_PERIPH_MANUAL_RESET BIT(1)
#define TEGRA_PERIPH_ON_APB BIT(2)
#define TEGRA_PERIPH_WAR_1005168 BIT(3)
void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert);
extern const struct clk_ops tegra_clk_periph_gate_ops;
......@@ -349,21 +419,22 @@ extern const struct clk_ops tegra_clk_periph_ops;
struct clk *tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset);
u32 offset, unsigned long flags);
struct clk *tegra_clk_register_periph_nodiv(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset);
#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \
#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \
_div_shift, _div_width, _div_frac_width, \
_div_flags, _clk_num, _enb_refcnt, _regs, \
_gate_flags) \
_gate_flags, _table) \
{ \
.mux = { \
.flags = _mux_flags, \
.shift = _mux_shift, \
.width = _mux_width, \
.mask = _mux_mask, \
.table = _table, \
}, \
.divider = { \
.flags = _div_flags, \
......@@ -391,28 +462,41 @@ struct tegra_periph_init_data {
u32 offset;
const char *con_id;
const char *dev_id;
unsigned long flags;
};
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \
_mux_shift, _mux_width, _mux_flags, _div_shift, \
#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_mask, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
_clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\
_flags) \
{ \
.name = _name, \
.clk_id = _clk_id, \
.parent_names = _parent_names, \
.num_parents = ARRAY_SIZE(_parent_names), \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \
_mux_flags, _div_shift, \
_div_width, _div_frac_width, \
_div_flags, _clk_num, \
_enb_refcnt, _regs, \
_gate_flags), \
_gate_flags, _table), \
.offset = _offset, \
.con_id = _con_id, \
.dev_id = _dev_id, \
.flags = _flags \
}
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_width, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, BIT(_mux_width) - 1, _mux_flags, \
_div_shift, _div_width, _div_frac_width, _div_flags, \
_regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\
NULL, 0)
/**
* struct clk_super_mux - super clock
*
......@@ -499,4 +583,13 @@ void tegra30_clock_init(struct device_node *np);
static inline void tegra30_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA_3x_SOC */
#ifdef CONFIG_ARCH_TEGRA_114_SOC
void tegra114_clock_init(struct device_node *np);
#else
static inline void tegra114_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA114_SOC */
typedef void (*tegra_clk_apply_init_table_func)(void);
extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
#endif /* TEGRA_CLK_H */
This diff is collapsed.
......@@ -23,7 +23,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sunxi_timer.h>
#include <linux/clk-provider.h>
#include <linux/clk/sunxi.h>
#define TIMER_CTL_REG 0x00
#define TIMER_CTL_ENABLE (1 << 0)
......@@ -123,7 +123,7 @@ void __init sunxi_timer_init(void)
if (irq <= 0)
panic("Can't parse IRQ");
of_clk_init(NULL);
sunxi_init_clocks();
clk = of_clk_get(node, 0);
if (IS_ERR(clk))
......
......@@ -172,7 +172,7 @@ static void __init tegra20_init_timer(struct device_node *np)
BUG();
}
clk = clk_get_sys("timer", NULL);
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");
rate = 12000000;
......@@ -235,7 +235,7 @@ static void __init tegra20_init_rtc(struct device_node *np)
* rtc registers are used by read_persistent_clock, keep the rtc clock
* enabled
*/
clk = clk_get_sys("rtc-tegra", NULL);
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
pr_warn("Unable to get rtc-tegra clock\n");
else
......
This diff is collapsed.
......@@ -152,7 +152,7 @@ struct clk {
}, \
.reg = _reg, \
.shift = _shift, \
.width = _width, \
.mask = BIT(_width) - 1, \
.flags = _mux_flags, \
.lock = _lock, \
}; \
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment