ARM: tegra: Common Clock Framework rework

Tegra already supports the common clock framework, but had issues:
 
 1) The clock driver was located in arch/arm/mach-tegra/ rather than
    drivers/clk/.
 
 2) A single "Tegra clock" type was implemented, rather than separate
    clock types for PLL, mux, divider, ... type in HW.
 
 3) Clock lookups by device drivers were still driven by device name
    and connection ID, rather than through device tree.
 
 This pull request solves all three issues. This required some DT changes
 to add clocks properties, and driver changes to request clocks more
 "correctly". Finally, this rework allows all AUXDATA to be removed from
 Tegra board files, and various duplicate clock lookup entries to be
 removed from the driver.
 
 This pull request is based on the previous pull request, with tag
 tegra-for-3.9-cleanup.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJRCYtBAAoJEMzrak5tbycxKb8P/0cXt2X7mPfoApWV96bI2c9h
 VE1wZYREcq0Au3hiNuMmPp1Nwous2zvrXRKXMvLoQi42KwpvZlFjlyn8+xACKmxO
 okSJ+aXETzlGh85l5RlnFJMgq181Kn0nDhN5Iwy0FUEJ8/oqdS8fEz5mwQlHflX1
 CLaquDVr/edr8LffvsFlxtSmeYNvZ2jYkSgroWeDhVR5Np1/LUCyh5y3edjVl/es
 B0/keuZ2fnYZnEfqLTpBEARYDBimymuu8gIoHK5nvtz3d/GGu92sVeda4LuHt8eH
 1N+f41ceDR2JG/MIJbLr6PGYmCkAGSM/5Vcfa33G+A7GQT0EVb8jLozGCdrCjaEG
 OM33pN5wtv1M9gTLR9swITBWhbTpRWaHnXeZQF7ttaV8dvr/fuOzWBw47k8Jw0FJ
 zjGta66kwW7WkT3HDNoM2RRzm9dlJr1xdHOzAaVJnX3VHtHcIvYzDi90Xv9Nn46D
 E/qIpExmL4rMrb2+4MxT9CdbfzdBSmsnlRFoWZTIM1NPxA/97i7oAyYVAJ34LCNx
 xWqwimhXK14LzGffpSHm9CSz8DHNbehDZRMQD0jGYMn61PFtDB+E/oEq5AEqneuC
 KDht3Qdx/mPzJQPE8WV3d5FxeXfXDjj203x/i6x8TOdH8Bt4aoK9ajvPYBpA+2aE
 4fPJIobLHGYN/F+GF1VJ
 =s9hz
 -----END PGP SIGNATURE-----

Merge tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc

From Stephen Warren:
ARM: tegra: Common Clock Framework rework

Tegra already supports the common clock framework, but had issues:

1) The clock driver was located in arch/arm/mach-tegra/ rather than
   drivers/clk/.

2) A single "Tegra clock" type was implemented, rather than separate
   clock types for PLL, mux, divider, ... type in HW.

3) Clock lookups by device drivers were still driven by device name
   and connection ID, rather than through device tree.

This pull request solves all three issues. This required some DT changes
to add clocks properties, and driver changes to request clocks more
"correctly". Finally, this rework allows all AUXDATA to be removed from
Tegra board files, and various duplicate clock lookup entries to be
removed from the driver.

This pull request is based on the previous pull request, with tag
tegra-for-3.9-cleanup.

* tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: (31 commits)
  clk: tegra30: remove unused TEGRA_CLK_DUPLICATE()s
  clk: tegra20: remove unused TEGRA_CLK_DUPLICATE()s
  ARM: tegra30: remove auxdata
  ARM: tegra20: remove auxdata
  ASoC: tegra: remove auxdata
  staging: nvec: remove use of clk_get_sys
  ARM: tegra: paz00: add clock information to DT
  ARM: tegra: add clock properties to Tegra30 DT
  ARM: tegra: add clock properties to Tegra20 DT
  spi: tegra: do not use clock name to get clock
  ARM: tegra: remove legacy clock code
  ARM: tegra: migrate to new clock code
  clk: tegra: add clock support for Tegra30
  clk: tegra: add clock support for Tegra20
  clk: tegra: add Tegra specific clocks
  ARM: tegra: define Tegra30 CAR binding
  ARM: tegra: define Tegra20 CAR binding
  ARM: tegra: move tegra_cpu_car.h to linux/clk/tegra.h
  ARM: tegra: add function to read chipid
  ARM: tegra: fix compile error when disable CPU_IDLE
  ...

Signed-off-by: Olof Johansson <olof@lixom.net>

Conflicts:
	arch/arm/mach-tegra/board-dt-tegra20.c
	arch/arm/mach-tegra/board-dt-tegra30.c
	arch/arm/mach-tegra/common.c
	arch/arm/mach-tegra/platsmp.c
	drivers/clocksource/Makefile
This commit is contained in:
Olof Johansson 2013-02-05 12:13:10 -08:00
commit bda6f8e6cd
67 changed files with 6426 additions and 7651 deletions

View file

@ -0,0 +1,205 @@
NVIDIA Tegra20 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,tegra20-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 96 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 95 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 96 and
above.
0 cpu
1 unassigned
2 unassigned
3 ac97
4 rtc
5 tmr
6 uart1
7 unassigned (register bit affects uart2 and vfir)
8 gpio
9 sdmmc2
10 unassigned (register bit affects spdif_in and spdif_out)
11 i2s1
12 i2c1
13 ndflash
14 sdmmc1
15 sdmmc4
16 twc
17 pwm
18 i2s2
19 epp
20 unassigned (register bit affects vi and vi_sensor)
21 2d
22 usbd
23 isp
24 3d
25 ide
26 disp2
27 disp1
28 host1x
29 vcp
30 unassigned
31 cache2
32 mem
33 ahbdma
34 apbdma
35 unassigned
36 kbc
37 stat_mon
38 pmc
39 fuse
40 kfuse
41 sbc1
42 snor
43 spi1
44 sbc2
45 xio
46 sbc3
47 dvc
48 dsi
49 unassigned (register bit affects tvo and cve)
50 mipi
51 hdmi
52 csi
53 tvdac
54 i2c2
55 uart3
56 unassigned
57 emc
58 usb2
59 usb3
60 mpe
61 vde
62 bsea
63 bsev
64 speedo
65 uart4
66 uart5
67 i2c3
68 sbc4
69 sdmmc3
70 pcie
71 owr
72 afi
73 csite
74 unassigned
75 avpucq
76 la
77 unassigned
78 unassigned
79 unassigned
80 unassigned
81 unassigned
82 unassigned
83 unassigned
84 irama
85 iramb
86 iramc
87 iramd
88 cram2
89 audio_2x a/k/a audio_2x_sync_clk
90 clk_d
91 unassigned
92 sus
93 cdev1
94 cdev2
95 unassigned
96 uart2
97 vfir
98 spdif_in
99 spdif_out
100 vi
101 vi_sensor
102 tvo
103 cve
104 osc
105 clk_32k a/k/a clk_s
106 clk_m
107 sclk
108 cclk
109 hclk
110 pclk
111 blink
112 pll_a
113 pll_a_out0
114 pll_c
115 pll_c_out1
116 pll_d
117 pll_d_out0
118 pll_e
119 pll_m
120 pll_m_out1
121 pll_p
122 pll_p_out1
123 pll_p_out2
124 pll_p_out3
125 pll_p_out4
126 pll_s
127 pll_u
128 pll_x
129 cop a/k/a avp
130 audio a/k/a audio_sync_clk
131 pll_ref
132 twd
Example SoC include file:
/ {
tegra_car: clock {
compatible = "nvidia,tegra20-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>;
};
};

View file

@ -0,0 +1,262 @@
NVIDIA Tegra30 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,tegra30-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 130 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 cpu
1 unassigned
2 unassigned
3 unassigned
4 rtc
5 timer
6 uarta
7 unassigned (register bit affects uartb and vfir)
8 gpio
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 cop_cache
32 mc
33 ahbdma
34 apbdma
35 unassigned
36 kbc
37 statmon
38 pmc
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 (register bit affects cve and tvo)
50 mipi
51 hdmi
52 csi
53 tvdac
54 i2c2
55 uartc
56 unassigned
57 emc
58 usb2
59 usb3
60 mpe
61 vde
62 bsea
63 bsev
64 speedo
65 uartd
66 uarte
67 i2c3
68 sbc4
69 sdmmc3
70 pcie
71 owr
72 afi
73 csite
74 pciex
75 avpucq
76 la
77 unassigned
78 unassigned
79 dtv
80 ndspeed
81 i2cslow
82 dsib
83 unassigned
84 irama
85 iramb
86 iramc
87 iramd
88 cram2
89 unassigned
90 audio_2x a/k/a audio_2x_sync_clk
91 unassigned
92 csus
93 cdev2
94 cdev1
95 unassigned
96 cpu_g
97 cpu_lp
98 3d2
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 atomics
113 audio0_2x
114 audio1_2x
115 audio2_2x
116 audio3_2x
117 audio4_2x
118 audio5_2x
119 actmon
120 extern1
121 extern2
122 extern3
123 sata_oob
124 sata
125 hda
127 se
128 hda2hdmi
129 sata_cold
160 uartb
161 vfir
162 spdif_in
163 spdif_out
164 vi
165 vi_sensor
166 fuse
167 fuse_burn
168 cve
169 tvo
170 clk_32k
171 clk_m
172 clk_m_div2
173 clk_m_div4
174 pll_ref
175 pll_c
176 pll_c_out1
177 pll_m
178 pll_m_out1
179 pll_p
180 pll_p_out1
181 pll_p_out2
182 pll_p_out3
183 pll_p_out4
184 pll_a
185 pll_a_out0
186 pll_d
187 pll_d_out0
188 pll_d2
189 pll_d2_out0
190 pll_u
191 pll_x
192 pll_x_out0
193 pll_e
194 spdif_in_sync
195 i2s0_sync
196 i2s1_sync
197 i2s2_sync
198 i2s3_sync
199 i2s4_sync
200 vimclk
201 audio0
202 audio1
203 audio2
204 audio3
205 audio4
206 audio5
207 clk_out_1 (extern1)
208 clk_out_2 (extern2)
209 clk_out_3 (extern3)
210 sclk
211 blink
212 cclk_g
213 cclk_lp
214 twd
215 cml0
216 cml1
217 hclk
218 pclk
Example SoC include file:
/ {
tegra_car: clock {
compatible = "nvidia,tegra30-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>;
};
};

View file

@ -643,6 +643,7 @@ config ARCH_TEGRA
select ARCH_HAS_CPUFREQ
select CLKDEV_LOOKUP
select CLKSRC_MMIO
select CLKSRC_OF
select COMMON_CLK
select GENERIC_CLOCKEVENTS
select GENERIC_GPIO

View file

@ -266,6 +266,8 @@ nvec {
clock-frequency = <80000>;
request-gpios = <&gpio 170 0>; /* gpio PV2 */
slave-addr = <138>;
clocks = <&tegra_car 67>, <&tegra_car 124>;
clock-names = "div-clk", "fast-clk";
};
i2c@7000d000 {

View file

@ -9,6 +9,7 @@ host1x {
reg = <0x50000000 0x00024000>;
interrupts = <0 65 0x04 /* mpcore syncpt */
0 67 0x04>; /* mpcore general */
clocks = <&tegra_car 28>;
#address-cells = <1>;
#size-cells = <1>;
@ -19,41 +20,49 @@ mpe {
compatible = "nvidia,tegra20-mpe";
reg = <0x54040000 0x00040000>;
interrupts = <0 68 0x04>;
clocks = <&tegra_car 60>;
};
vi {
compatible = "nvidia,tegra20-vi";
reg = <0x54080000 0x00040000>;
interrupts = <0 69 0x04>;
clocks = <&tegra_car 100>;
};
epp {
compatible = "nvidia,tegra20-epp";
reg = <0x540c0000 0x00040000>;
interrupts = <0 70 0x04>;
clocks = <&tegra_car 19>;
};
isp {
compatible = "nvidia,tegra20-isp";
reg = <0x54100000 0x00040000>;
interrupts = <0 71 0x04>;
clocks = <&tegra_car 23>;
};
gr2d {
compatible = "nvidia,tegra20-gr2d";
reg = <0x54140000 0x00040000>;
interrupts = <0 72 0x04>;
clocks = <&tegra_car 21>;
};
gr3d {
compatible = "nvidia,tegra20-gr3d";
reg = <0x54180000 0x00040000>;
clocks = <&tegra_car 24>;
};
dc@54200000 {
compatible = "nvidia,tegra20-dc";
reg = <0x54200000 0x00040000>;
interrupts = <0 73 0x04>;
clocks = <&tegra_car 27>, <&tegra_car 121>;
clock-names = "disp1", "parent";
rgb {
status = "disabled";
@ -64,6 +73,8 @@ dc@54240000 {
compatible = "nvidia,tegra20-dc";
reg = <0x54240000 0x00040000>;
interrupts = <0 74 0x04>;
clocks = <&tegra_car 26>, <&tegra_car 121>;
clock-names = "disp2", "parent";
rgb {
status = "disabled";
@ -74,6 +85,8 @@ hdmi {
compatible = "nvidia,tegra20-hdmi";
reg = <0x54280000 0x00040000>;
interrupts = <0 75 0x04>;
clocks = <&tegra_car 51>, <&tegra_car 117>;
clock-names = "hdmi", "parent";
status = "disabled";
};
@ -81,12 +94,14 @@ tvo {
compatible = "nvidia,tegra20-tvo";
reg = <0x542c0000 0x00040000>;
interrupts = <0 76 0x04>;
clocks = <&tegra_car 102>;
status = "disabled";
};
dsi {
compatible = "nvidia,tegra20-dsi";
reg = <0x54300000 0x00040000>;
clocks = <&tegra_car 48>;
status = "disabled";
};
};
@ -123,6 +138,12 @@ timer@60005000 {
0 42 0x04>;
};
tegra_car: clock {
compatible = "nvidia,tegra20-car";
reg = <0x60006000 0x1000>;
#clock-cells = <1>;
};
apbdma: dma {
compatible = "nvidia,tegra20-apbdma";
reg = <0x6000a000 0x1200>;
@ -142,6 +163,7 @@ apbdma: dma {
0 117 0x04
0 118 0x04
0 119 0x04>;
clocks = <&tegra_car 34>;
};
ahb {
@ -183,6 +205,7 @@ tegra_i2s1: i2s@70002800 {
reg = <0x70002800 0x200>;
interrupts = <0 13 0x04>;
nvidia,dma-request-selector = <&apbdma 2>;
clocks = <&tegra_car 11>;
status = "disabled";
};
@ -191,6 +214,7 @@ tegra_i2s2: i2s@70002a00 {
reg = <0x70002a00 0x200>;
interrupts = <0 3 0x04>;
nvidia,dma-request-selector = <&apbdma 1>;
clocks = <&tegra_car 18>;
status = "disabled";
};
@ -199,6 +223,7 @@ serial@70006000 {
reg = <0x70006000 0x40>;
reg-shift = <2>;
interrupts = <0 36 0x04>;
clocks = <&tegra_car 6>;
status = "disabled";
};
@ -207,6 +232,7 @@ serial@70006040 {
reg = <0x70006040 0x40>;
reg-shift = <2>;
interrupts = <0 37 0x04>;
clocks = <&tegra_car 96>;
status = "disabled";
};
@ -215,6 +241,7 @@ serial@70006200 {
reg = <0x70006200 0x100>;
reg-shift = <2>;
interrupts = <0 46 0x04>;
clocks = <&tegra_car 55>;
status = "disabled";
};
@ -223,6 +250,7 @@ serial@70006300 {
reg = <0x70006300 0x100>;
reg-shift = <2>;
interrupts = <0 90 0x04>;
clocks = <&tegra_car 65>;
status = "disabled";
};
@ -231,6 +259,7 @@ serial@70006400 {
reg = <0x70006400 0x100>;
reg-shift = <2>;
interrupts = <0 91 0x04>;
clocks = <&tegra_car 66>;
status = "disabled";
};
@ -238,6 +267,7 @@ pwm: pwm {
compatible = "nvidia,tegra20-pwm";
reg = <0x7000a000 0x100>;
#pwm-cells = <2>;
clocks = <&tegra_car 17>;
};
rtc {
@ -252,6 +282,8 @@ i2c@7000c000 {
interrupts = <0 38 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 12>, <&tegra_car 124>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -262,6 +294,7 @@ spi@7000c380 {
nvidia,dma-request-selector = <&apbdma 11>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 43>;
status = "disabled";
};
@ -271,6 +304,8 @@ i2c@7000c400 {
interrupts = <0 84 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 54>, <&tegra_car 124>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -280,6 +315,8 @@ i2c@7000c500 {
interrupts = <0 92 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 67>, <&tegra_car 124>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -289,6 +326,8 @@ i2c@7000d000 {
interrupts = <0 53 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 47>, <&tegra_car 124>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -299,6 +338,7 @@ spi@7000d400 {
nvidia,dma-request-selector = <&apbdma 15>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 41>;
status = "disabled";
};
@ -309,6 +349,7 @@ spi@7000d600 {
nvidia,dma-request-selector = <&apbdma 16>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 44>;
status = "disabled";
};
@ -319,6 +360,7 @@ spi@7000d800 {
nvidia,dma-request-selector = <&apbdma 17>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 46>;
status = "disabled";
};
@ -329,6 +371,7 @@ spi@7000da00 {
nvidia,dma-request-selector = <&apbdma 18>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 68>;
status = "disabled";
};
@ -363,6 +406,7 @@ usb@c5000000 {
interrupts = <0 20 0x04>;
phy_type = "utmi";
nvidia,has-legacy-mode;
clocks = <&tegra_car 22>;
status = "disabled";
};
@ -371,6 +415,7 @@ usb@c5004000 {
reg = <0xc5004000 0x4000>;
interrupts = <0 21 0x04>;
phy_type = "ulpi";
clocks = <&tegra_car 58>;
status = "disabled";
};
@ -379,6 +424,7 @@ usb@c5008000 {
reg = <0xc5008000 0x4000>;
interrupts = <0 97 0x04>;
phy_type = "utmi";
clocks = <&tegra_car 59>;
status = "disabled";
};
@ -386,6 +432,7 @@ sdhci@c8000000 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000000 0x200>;
interrupts = <0 14 0x04>;
clocks = <&tegra_car 14>;
status = "disabled";
};
@ -393,6 +440,7 @@ sdhci@c8000200 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000200 0x200>;
interrupts = <0 15 0x04>;
clocks = <&tegra_car 9>;
status = "disabled";
};
@ -400,6 +448,7 @@ sdhci@c8000400 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000400 0x200>;
interrupts = <0 19 0x04>;
clocks = <&tegra_car 69>;
status = "disabled";
};
@ -407,6 +456,7 @@ sdhci@c8000600 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000600 0x200>;
interrupts = <0 31 0x04>;
clocks = <&tegra_car 15>;
status = "disabled";
};

View file

@ -9,6 +9,7 @@ host1x {
reg = <0x50000000 0x00024000>;
interrupts = <0 65 0x04 /* mpcore syncpt */
0 67 0x04>; /* mpcore general */
clocks = <&tegra_car 28>;
#address-cells = <1>;
#size-cells = <1>;
@ -19,41 +20,50 @@ mpe {
compatible = "nvidia,tegra30-mpe";
reg = <0x54040000 0x00040000>;
interrupts = <0 68 0x04>;
clocks = <&tegra_car 60>;
};
vi {
compatible = "nvidia,tegra30-vi";
reg = <0x54080000 0x00040000>;
interrupts = <0 69 0x04>;
clocks = <&tegra_car 164>;
};
epp {
compatible = "nvidia,tegra30-epp";
reg = <0x540c0000 0x00040000>;
interrupts = <0 70 0x04>;
clocks = <&tegra_car 19>;
};
isp {
compatible = "nvidia,tegra30-isp";
reg = <0x54100000 0x00040000>;
interrupts = <0 71 0x04>;
clocks = <&tegra_car 23>;
};
gr2d {
compatible = "nvidia,tegra30-gr2d";
reg = <0x54140000 0x00040000>;
interrupts = <0 72 0x04>;
clocks = <&tegra_car 21>;
};
gr3d {
compatible = "nvidia,tegra30-gr3d";
reg = <0x54180000 0x00040000>;
clocks = <&tegra_car 24 &tegra_car 98>;
clock-names = "3d", "3d2";
};
dc@54200000 {
compatible = "nvidia,tegra30-dc";
reg = <0x54200000 0x00040000>;
interrupts = <0 73 0x04>;
clocks = <&tegra_car 27>, <&tegra_car 179>;
clock-names = "disp1", "parent";
rgb {
status = "disabled";
@ -64,6 +74,8 @@ dc@54240000 {
compatible = "nvidia,tegra30-dc";
reg = <0x54240000 0x00040000>;
interrupts = <0 74 0x04>;
clocks = <&tegra_car 26>, <&tegra_car 179>;
clock-names = "disp2", "parent";
rgb {
status = "disabled";
@ -74,6 +86,8 @@ hdmi {
compatible = "nvidia,tegra30-hdmi";
reg = <0x54280000 0x00040000>;
interrupts = <0 75 0x04>;
clocks = <&tegra_car 51>, <&tegra_car 189>;
clock-names = "hdmi", "parent";
status = "disabled";
};
@ -81,12 +95,14 @@ tvo {
compatible = "nvidia,tegra30-tvo";
reg = <0x542c0000 0x00040000>;
interrupts = <0 76 0x04>;
clocks = <&tegra_car 169>;
status = "disabled";
};
dsi {
compatible = "nvidia,tegra30-dsi";
reg = <0x54300000 0x00040000>;
clocks = <&tegra_car 48>;
status = "disabled";
};
};
@ -125,6 +141,12 @@ timer@60005000 {
0 122 0x04>;
};
tegra_car: clock {
compatible = "nvidia,tegra30-car";
reg = <0x60006000 0x1000>;
#clock-cells = <1>;
};
apbdma: dma {
compatible = "nvidia,tegra30-apbdma", "nvidia,tegra20-apbdma";
reg = <0x6000a000 0x1400>;
@ -160,6 +182,7 @@ apbdma: dma {
0 141 0x04
0 142 0x04
0 143 0x04>;
clocks = <&tegra_car 34>;
};
ahb: ahb {
@ -195,6 +218,7 @@ serial@70006000 {
reg = <0x70006000 0x40>;
reg-shift = <2>;
interrupts = <0 36 0x04>;
clocks = <&tegra_car 6>;
status = "disabled";
};
@ -203,6 +227,7 @@ serial@70006040 {
reg = <0x70006040 0x40>;
reg-shift = <2>;
interrupts = <0 37 0x04>;
clocks = <&tegra_car 160>;
status = "disabled";
};
@ -211,6 +236,7 @@ serial@70006200 {
reg = <0x70006200 0x100>;
reg-shift = <2>;
interrupts = <0 46 0x04>;
clocks = <&tegra_car 55>;
status = "disabled";
};
@ -219,6 +245,7 @@ serial@70006300 {
reg = <0x70006300 0x100>;
reg-shift = <2>;
interrupts = <0 90 0x04>;
clocks = <&tegra_car 65>;
status = "disabled";
};
@ -227,6 +254,7 @@ serial@70006400 {
reg = <0x70006400 0x100>;
reg-shift = <2>;
interrupts = <0 91 0x04>;
clocks = <&tegra_car 66>;
status = "disabled";
};
@ -234,6 +262,7 @@ pwm: pwm {
compatible = "nvidia,tegra30-pwm", "nvidia,tegra20-pwm";
reg = <0x7000a000 0x100>;
#pwm-cells = <2>;
clocks = <&tegra_car 17>;
};
rtc {
@ -248,6 +277,8 @@ i2c@7000c000 {
interrupts = <0 38 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 12>, <&tegra_car 182>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -257,6 +288,8 @@ i2c@7000c400 {
interrupts = <0 84 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 54>, <&tegra_car 182>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -266,6 +299,8 @@ i2c@7000c500 {
interrupts = <0 92 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 67>, <&tegra_car 182>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -275,6 +310,8 @@ i2c@7000c700 {
interrupts = <0 120 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 103>, <&tegra_car 182>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -284,6 +321,8 @@ i2c@7000d000 {
interrupts = <0 53 0x04>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 47>, <&tegra_car 182>;
clock-names = "div-clk", "fast-clk";
status = "disabled";
};
@ -294,6 +333,7 @@ spi@7000d400 {
nvidia,dma-request-selector = <&apbdma 15>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 41>;
status = "disabled";
};
@ -304,6 +344,7 @@ spi@7000d600 {
nvidia,dma-request-selector = <&apbdma 16>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 44>;
status = "disabled";
};
@ -314,6 +355,7 @@ spi@7000d800 {
nvidia,dma-request-selector = <&apbdma 17>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 46>;
status = "disabled";
};
@ -324,6 +366,7 @@ spi@7000da00 {
nvidia,dma-request-selector = <&apbdma 18>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 68>;
status = "disabled";
};
@ -334,6 +377,7 @@ spi@7000dc00 {
nvidia,dma-request-selector = <&apbdma 27>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 104>;
status = "disabled";
};
@ -344,6 +388,7 @@ spi@7000de00 {
nvidia,dma-request-selector = <&apbdma 28>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&tegra_car 105>;
status = "disabled";
};
@ -377,7 +422,13 @@ ahub {
0x70080200 0x100>;
interrupts = <0 103 0x04>;
nvidia,dma-request-selector = <&apbdma 1>;
clocks = <&tegra_car 106>, <&tegra_car 107>, <&tegra_car 30>,
<&tegra_car 11>, <&tegra_car 18>, <&tegra_car 101>,
<&tegra_car 102>, <&tegra_car 108>, <&tegra_car 109>,
<&tegra_car 110>, <&tegra_car 162>;
clock-names = "d_audio", "apbif", "i2s0", "i2s1", "i2s2",
"i2s3", "i2s4", "dam0", "dam1", "dam2",
"spdif_in";
ranges;
#address-cells = <1>;
#size-cells = <1>;
@ -386,6 +437,7 @@ tegra_i2s0: i2s@70080300 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080300 0x100>;
nvidia,ahub-cif-ids = <4 4>;
clocks = <&tegra_car 30>;
status = "disabled";
};
@ -393,6 +445,7 @@ tegra_i2s1: i2s@70080400 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080400 0x100>;
nvidia,ahub-cif-ids = <5 5>;
clocks = <&tegra_car 11>;
status = "disabled";
};
@ -400,6 +453,7 @@ tegra_i2s2: i2s@70080500 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080500 0x100>;
nvidia,ahub-cif-ids = <6 6>;
clocks = <&tegra_car 18>;
status = "disabled";
};
@ -407,6 +461,7 @@ tegra_i2s3: i2s@70080600 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080600 0x100>;
nvidia,ahub-cif-ids = <7 7>;
clocks = <&tegra_car 101>;
status = "disabled";
};
@ -414,6 +469,7 @@ tegra_i2s4: i2s@70080700 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080700 0x100>;
nvidia,ahub-cif-ids = <8 8>;
clocks = <&tegra_car 102>;
status = "disabled";
};
};
@ -422,6 +478,7 @@ sdhci@78000000 {
compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
reg = <0x78000000 0x200>;
interrupts = <0 14 0x04>;
clocks = <&tegra_car 14>;
status = "disabled";
};
@ -429,6 +486,7 @@ sdhci@78000200 {
compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
reg = <0x78000200 0x200>;
interrupts = <0 15 0x04>;
clocks = <&tegra_car 9>;
status = "disabled";
};
@ -436,6 +494,7 @@ sdhci@78000400 {
compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
reg = <0x78000400 0x200>;
interrupts = <0 19 0x04>;
clocks = <&tegra_car 69>;
status = "disabled";
};
@ -443,6 +502,7 @@ sdhci@78000600 {
compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
reg = <0x78000600 0x200>;
interrupts = <0 31 0x04>;
clocks = <&tegra_car 15>;
status = "disabled";
};

View file

@ -6,9 +6,9 @@ config ARCH_TEGRA_2x_SOC
bool "Enable support for Tegra20 family"
select ARCH_REQUIRE_GPIOLIB
select ARM_ERRATA_720789
select ARM_ERRATA_742230
select ARM_ERRATA_742230 if SMP
select ARM_ERRATA_751472
select ARM_ERRATA_754327
select ARM_ERRATA_754327 if SMP
select ARM_ERRATA_764369 if SMP
select ARM_GIC
select CPU_FREQ_TABLE if CPU_FREQ

View file

@ -1,33 +1,28 @@
obj-y += common.o
obj-y += io.o
obj-y += irq.o
obj-y += clock.o
obj-y += timer.o
obj-y += fuse.o
obj-y += pmc.o
obj-y += flowctrl.o
obj-y += powergate.o
obj-y += apbio.o
obj-y += pm.o
obj-y += reset.o
obj-y += reset-handler.o
obj-y += sleep.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_CPU_IDLE) += sleep.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o
endif
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o
endif
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_SMP) += reset.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
obj-$(CONFIG_TEGRA_PCI) += pcie.o

View file

@ -38,7 +38,7 @@ static void tegra_apb_writel_direct(u32 value, unsigned long offset);
static struct dma_chan *tegra_apb_dma_chan;
static struct dma_slave_config dma_sconfig;
bool tegra_apb_dma_init(void)
static bool tegra_apb_dma_init(void)
{
dma_cap_mask_t mask;

View file

@ -15,6 +15,7 @@
*
*/
#include <linux/clocksource.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
@ -39,99 +40,45 @@
#include <asm/setup.h>
#include "board.h"
#include "clock.h"
#include "common.h"
#include "iomap.h"
struct tegra_ehci_platform_data tegra_ehci1_pdata = {
static struct tegra_ehci_platform_data tegra_ehci1_pdata = {
.operating_mode = TEGRA_USB_OTG,
.power_down_on_bus_suspend = 1,
.vbus_gpio = -1,
};
struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
.reset_gpio = -1,
.clk = "cdev2",
};
struct tegra_ehci_platform_data tegra_ehci2_pdata = {
static struct tegra_ehci_platform_data tegra_ehci2_pdata = {
.phy_config = &tegra_ehci2_ulpi_phy_config,
.operating_mode = TEGRA_USB_HOST,
.power_down_on_bus_suspend = 1,
.vbus_gpio = -1,
};
struct tegra_ehci_platform_data tegra_ehci3_pdata = {
static struct tegra_ehci_platform_data tegra_ehci3_pdata = {
.operating_mode = TEGRA_USB_HOST,
.power_down_on_bus_suspend = 1,
.vbus_gpio = -1,
};
struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC1_BASE, "sdhci-tegra.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC2_BASE, "sdhci-tegra.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC3_BASE, "sdhci-tegra.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC4_BASE, "sdhci-tegra.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C_BASE, "tegra-i2c.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C2_BASE, "tegra-i2c.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C3_BASE, "tegra-i2c.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c-dvc", TEGRA_DVC_BASE, "tegra-i2c.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra20-i2s.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra20-i2s.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra20-das", NULL),
static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0",
&tegra_ehci1_pdata),
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1",
&tegra_ehci2_pdata),
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
&tegra_ehci3_pdata),
OF_DEV_AUXDATA("nvidia,tegra20-apbdma", TEGRA_APB_DMA_BASE, "tegra-apbdma", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sflash", 0x7000c380, "spi", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D400, "spi_tegra.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D600, "spi_tegra.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D800, "spi_tegra.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000DA00, "spi_tegra.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-host1x", 0x50000000, "host1x", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-dc", 0x54200000, "tegradc.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-dc", 0x54240000, "tegradc.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-hdmi", 0x54280000, "hdmi", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-dsi", 0x54300000, "dsi", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-tvo", 0x542c0000, "tvo", NULL),
{}
};
static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
/* name parent rate enabled */
{ "uarta", "pll_p", 216000000, true },
{ "uartd", "pll_p", 216000000, true },
{ "usbd", "clk_m", 12000000, false },
{ "usb2", "clk_m", 12000000, false },
{ "usb3", "clk_m", 12000000, false },
{ "pll_a", "pll_p_out1", 56448000, true },
{ "pll_a_out0", "pll_a", 11289600, true },
{ "cdev1", NULL, 0, true },
{ "blink", "clk_32k", 32768, true },
{ "i2s1", "pll_a_out0", 11289600, false},
{ "i2s2", "pll_a_out0", 11289600, false},
{ "sdmmc1", "pll_p", 48000000, false},
{ "sdmmc3", "pll_p", 48000000, false},
{ "sdmmc4", "pll_p", 48000000, false},
{ "spi", "pll_p", 20000000, false },
{ "sbc1", "pll_p", 100000000, false },
{ "sbc2", "pll_p", 100000000, false },
{ "sbc3", "pll_p", 100000000, false },
{ "sbc4", "pll_p", 100000000, false },
{ "host1x", "pll_c", 150000000, false },
{ "disp1", "pll_p", 600000000, false },
{ "disp2", "pll_p", 600000000, false },
{ NULL, NULL, 0, 0},
};
static void __init tegra_dt_init(void)
{
tegra_clk_init_from_table(tegra_dt_clk_init_table);
/*
* Finished with the static registrations now; fill in the missing
* devices
@ -200,7 +147,7 @@ DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)")
.smp = smp_ops(tegra_smp_ops),
.init_early = tegra20_init_early,
.init_irq = tegra_dt_init_irq,
.init_time = tegra_init_timer,
.init_time = clocksource_of_init,
.init_machine = tegra_dt_init,
.init_late = tegra_dt_init_late,
.restart = tegra_assert_system_reset,

View file

@ -23,6 +23,7 @@
*
*/
#include <linux/clocksource.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
@ -33,72 +34,12 @@
#include <asm/mach/arch.h>
#include "board.h"
#include "clock.h"
#include "common.h"
#include "iomap.h"
struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000000, "sdhci-tegra.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000200, "sdhci-tegra.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000400, "sdhci-tegra.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000600, "sdhci-tegra.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C000, "tegra-i2c.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C400, "tegra-i2c.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C500, "tegra-i2c.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C700, "tegra-i2c.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000D000, "tegra-i2c.4", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-ahub", 0x70080000, "tegra30-ahub", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-apbdma", 0x6000a000, "tegra-apbdma", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D400, "spi_tegra.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D600, "spi_tegra.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D800, "spi_tegra.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DA00, "spi_tegra.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DC00, "spi_tegra.4", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DE00, "spi_tegra.5", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-host1x", 0x50000000, "host1x", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-dc", 0x54200000, "tegradc.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-dc", 0x54240000, "tegradc.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-hdmi", 0x54280000, "hdmi", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-dsi", 0x54300000, "dsi", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-tvo", 0x542c0000, "tvo", NULL),
{}
};
static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
/* name parent rate enabled */
{ "uarta", "pll_p", 408000000, true },
{ "pll_a", "pll_p_out1", 564480000, true },
{ "pll_a_out0", "pll_a", 11289600, true },
{ "extern1", "pll_a_out0", 0, true },
{ "clk_out_1", "extern1", 0, true },
{ "blink", "clk_32k", 32768, true },
{ "i2s0", "pll_a_out0", 11289600, false},
{ "i2s1", "pll_a_out0", 11289600, false},
{ "i2s2", "pll_a_out0", 11289600, false},
{ "i2s3", "pll_a_out0", 11289600, false},
{ "i2s4", "pll_a_out0", 11289600, false},
{ "sdmmc1", "pll_p", 48000000, false},
{ "sdmmc3", "pll_p", 48000000, false},
{ "sdmmc4", "pll_p", 48000000, false},
{ "sbc1", "pll_p", 100000000, false},
{ "sbc2", "pll_p", 100000000, false},
{ "sbc3", "pll_p", 100000000, false},
{ "sbc4", "pll_p", 100000000, false},
{ "sbc5", "pll_p", 100000000, false},
{ "sbc6", "pll_p", 100000000, false},
{ "host1x", "pll_c", 150000000, false},
{ "disp1", "pll_p", 600000000, false},
{ "disp2", "pll_p", 600000000, false},
{ NULL, NULL, 0, 0},
};
static void __init tegra30_dt_init(void)
{
tegra_clk_init_from_table(tegra_dt_clk_init_table);
of_platform_populate(NULL, of_default_bus_match_table,
tegra30_auxdata_lookup, NULL);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
static const char *tegra30_dt_board_compat[] = {
@ -111,7 +52,7 @@ DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)")
.map_io = tegra_map_common_io,
.init_early = tegra30_init_early,
.init_irq = tegra_dt_init_irq,
.init_time = tegra_init_timer,
.init_time = clocksource_of_init,
.init_machine = tegra30_dt_init,
.init_late = tegra_init_late,
.restart = tegra_assert_system_reset,

View file

@ -55,5 +55,4 @@ static inline int harmony_pcie_init(void) { return 0; }
void __init tegra_paz00_wifikill_init(void);
extern void tegra_init_timer(void);
#endif

View file

@ -1,166 +0,0 @@
/*
*
* Copyright (C) 2010 Google, Inc.
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@google.com>
*
* 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/kernel.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include "board.h"
#include "clock.h"
#include "tegra_cpu_car.h"
/* Global data of Tegra CPU CAR ops */
struct tegra_cpu_car_ops *tegra_cpu_car_ops;
/*
* Locking:
*
* An additional mutex, clock_list_lock, is used to protect the list of all
* clocks.
*
*/
static DEFINE_MUTEX(clock_list_lock);
static LIST_HEAD(clocks);
void tegra_clk_add(struct clk *clk)
{
struct clk_tegra *c = to_clk_tegra(__clk_get_hw(clk));
mutex_lock(&clock_list_lock);
list_add(&c->node, &clocks);
mutex_unlock(&clock_list_lock);
}
struct clk *tegra_get_clock_by_name(const char *name)
{
struct clk_tegra *c;
struct clk *ret = NULL;
mutex_lock(&clock_list_lock);
list_for_each_entry(c, &clocks, node) {
if (strcmp(__clk_get_name(c->hw.clk), name) == 0) {
ret = c->hw.clk;
break;
}
}
mutex_unlock(&clock_list_lock);
return ret;
}
static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
{
struct clk *c;
struct clk *p;
struct clk *parent;
int ret = 0;
c = tegra_get_clock_by_name(table->name);
if (!c) {
pr_warn("Unable to initialize clock %s\n",
table->name);
return -ENODEV;
}
parent = clk_get_parent(c);
if (table->parent) {
p = tegra_get_clock_by_name(table->parent);
if (!p) {
pr_warn("Unable to find parent %s of clock %s\n",
table->parent, table->name);
return -ENODEV;
}
if (parent != p) {
ret = clk_set_parent(c, p);
if (ret) {
pr_warn("Unable to set parent %s of clock %s: %d\n",
table->parent, table->name, ret);
return -EINVAL;
}
}
}
if (table->rate && table->rate != clk_get_rate(c)) {
ret = clk_set_rate(c, table->rate);
if (ret) {
pr_warn("Unable to set clock %s to rate %lu: %d\n",
table->name, table->rate, ret);
return -EINVAL;
}
}
if (table->enabled) {
ret = clk_prepare_enable(c);
if (ret) {
pr_warn("Unable to enable clock %s: %d\n",
table->name, ret);
return -EINVAL;
}
}
return 0;
}
void tegra_clk_init_from_table(struct tegra_clk_init_table *table)
{
for (; table->name; table++)
tegra_clk_init_one_from_table(table);
}
void tegra_periph_reset_deassert(struct clk *c)
{
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
BUG_ON(!clk->reset);
clk->reset(__clk_get_hw(c), false);
}
EXPORT_SYMBOL(tegra_periph_reset_deassert);
void tegra_periph_reset_assert(struct clk *c)
{
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
BUG_ON(!clk->reset);
clk->reset(__clk_get_hw(c), true);
}
EXPORT_SYMBOL(tegra_periph_reset_assert);
/* Several extended clock configuration bits (e.g., clock routing, clock
* phase control) are included in PLL and peripheral clock source
* registers. */
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
{
int ret = 0;
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
if (!clk->clk_cfg_ex) {
ret = -ENOSYS;
goto out;
}
ret = clk->clk_cfg_ex(__clk_get_hw(c), p, setting);
out:
return ret;
}

View file

@ -1,153 +0,0 @@
/*
* arch/arm/mach-tegra/include/mach/clock.h
*
* Copyright (C) 2010 Google, Inc.
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@google.com>
*
* 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.
*
*/
#ifndef __MACH_TEGRA_CLOCK_H
#define __MACH_TEGRA_CLOCK_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/list.h>
#include <mach/clk.h>
#define DIV_BUS (1 << 0)
#define DIV_U71 (1 << 1)
#define DIV_U71_FIXED (1 << 2)
#define DIV_2 (1 << 3)
#define DIV_U16 (1 << 4)
#define PLL_FIXED (1 << 5)
#define PLL_HAS_CPCON (1 << 6)
#define MUX (1 << 7)
#define PLLD (1 << 8)
#define PERIPH_NO_RESET (1 << 9)
#define PERIPH_NO_ENB (1 << 10)
#define PERIPH_EMC_ENB (1 << 11)
#define PERIPH_MANUAL_RESET (1 << 12)
#define PLL_ALT_MISC_REG (1 << 13)
#define PLLU (1 << 14)
#define PLLX (1 << 15)
#define MUX_PWM (1 << 16)
#define MUX8 (1 << 17)
#define DIV_U71_UART (1 << 18)
#define MUX_CLK_OUT (1 << 19)
#define PLLM (1 << 20)
#define DIV_U71_INT (1 << 21)
#define DIV_U71_IDLE (1 << 22)
#define ENABLE_ON_INIT (1 << 28)
#define PERIPH_ON_APB (1 << 29)
struct clk_tegra;
#define to_clk_tegra(_hw) container_of(_hw, struct clk_tegra, hw)
struct clk_mux_sel {
struct clk *input;
u32 value;
};
struct clk_pll_freq_table {
unsigned long input_rate;
unsigned long output_rate;
u16 n;
u16 m;
u8 p;
u8 cpcon;
};
enum clk_state {
UNINITIALIZED = 0,
ON,
OFF,
};
struct clk_tegra {
/* node for master clocks list */
struct list_head node; /* node for list of all clocks */
struct clk_lookup lookup;
struct clk_hw hw;
bool set;
unsigned long fixed_rate;
unsigned long max_rate;
unsigned long min_rate;
u32 flags;
const char *name;
enum clk_state state;
u32 div;
u32 mul;
u32 reg;
u32 reg_shift;
struct list_head shared_bus_list;
union {
struct {
unsigned int clk_num;
} periph;
struct {
unsigned long input_min;
unsigned long input_max;
unsigned long cf_min;
unsigned long cf_max;
unsigned long vco_min;
unsigned long vco_max;
const struct clk_pll_freq_table *freq_table;
int lock_delay;
unsigned long fixed_rate;
} pll;
struct {
u32 sel;
u32 reg_mask;
} mux;
struct {
struct clk *main;
struct clk *backup;
} cpu;
struct {
struct list_head node;
bool enabled;
unsigned long rate;
} shared_bus_user;
} u;
void (*reset)(struct clk_hw *, bool);
int (*clk_cfg_ex)(struct clk_hw *, enum tegra_clk_ex_param, u32);
};
struct clk_duplicate {
const char *name;
struct clk_lookup lookup;
};
struct tegra_clk_init_table {
const char *name;
const char *parent;
unsigned long rate;
bool enabled;
};
void tegra_clk_add(struct clk *c);
void tegra2_init_clocks(void);
void tegra30_init_clocks(void);
struct clk *tegra_get_clock_by_name(const char *name);
void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
#endif

View file

@ -22,13 +22,13 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/irqchip.h>
#include <linux/clk/tegra.h>
#include <asm/hardware/cache-l2x0.h>
#include <mach/powergate.h>
#include "board.h"
#include "clock.h"
#include "common.h"
#include "fuse.h"
#include "iomap.h"
@ -36,6 +36,7 @@
#include "apbio.h"
#include "sleep.h"
#include "pm.h"
#include "reset.h"
/*
* Storage for debug-macro.S's state.
@ -58,6 +59,7 @@ u32 tegra_uart_config[4] = {
#ifdef CONFIG_OF
void __init tegra_dt_init_irq(void)
{
tegra_clocks_init();
tegra_init_irq();
irqchip_init();
}
@ -73,43 +75,6 @@ void tegra_assert_system_reset(char mode, const char *cmd)
writel_relaxed(reg, reset);
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static __initdata struct tegra_clk_init_table tegra20_clk_init_table[] = {
/* name parent rate enabled */
{ "clk_m", NULL, 0, true },
{ "pll_p", "clk_m", 216000000, true },
{ "pll_p_out1", "pll_p", 28800000, true },
{ "pll_p_out2", "pll_p", 48000000, true },
{ "pll_p_out3", "pll_p", 72000000, true },
{ "pll_p_out4", "pll_p", 24000000, true },
{ "pll_c", "clk_m", 600000000, true },
{ "pll_c_out1", "pll_c", 120000000, true },
{ "sclk", "pll_c_out1", 120000000, true },
{ "hclk", "sclk", 120000000, true },
{ "pclk", "hclk", 60000000, true },
{ "csite", NULL, 0, true },
{ "emc", NULL, 0, true },
{ "cpu", NULL, 0, true },
{ NULL, NULL, 0, 0},
};
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
/* name parent rate enabled */
{ "clk_m", NULL, 0, true },
{ "pll_p", "pll_ref", 408000000, true },
{ "pll_p_out1", "pll_p", 9600000, true },
{ "pll_p_out4", "pll_p", 102000000, true },
{ "sclk", "pll_p_out4", 102000000, true },
{ "hclk", "sclk", 102000000, true },
{ "pclk", "hclk", 51000000, true },
{ "csite", NULL, 0, true },
{ NULL, NULL, 0, 0},
};
#endif
static void __init tegra_init_cache(void)
{
#ifdef CONFIG_CACHE_L2X0
@ -131,10 +96,9 @@ static void __init tegra_init_cache(void)
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
void __init tegra20_init_early(void)
{
tegra_cpu_reset_handler_init();
tegra_apb_io_init();
tegra_init_fuse();
tegra2_init_clocks();
tegra_clk_init_from_table(tegra20_clk_init_table);
tegra_init_cache();
tegra_pmc_init();
tegra_powergate_init();
@ -144,10 +108,9 @@ void __init tegra20_init_early(void)
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
void __init tegra30_init_early(void)
{
tegra_cpu_reset_handler_init();
tegra_apb_io_init();
tegra_init_fuse();
tegra30_init_clocks();
tegra_clk_init_from_table(tegra30_clk_init_table);
tegra_init_cache();
tegra_pmc_init();
tegra_powergate_init();

View file

@ -1,4 +1,5 @@
extern struct smp_operations tegra_smp_ops;
extern int tegra_cpu_kill(unsigned int cpu);
extern void tegra_cpu_die(unsigned int cpu);
extern int tegra_cpu_disable(unsigned int cpu);

View file

@ -214,24 +214,6 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
if (policy->cpu >= NUM_CPUS)
return -EINVAL;
cpu_clk = clk_get_sys(NULL, "cpu");
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
pll_x_clk = clk_get_sys(NULL, "pll_x");
if (IS_ERR(pll_x_clk))
return PTR_ERR(pll_x_clk);
pll_p_clk = clk_get_sys(NULL, "pll_p");
if (IS_ERR(pll_p_clk))
return PTR_ERR(pll_p_clk);
emc_clk = clk_get_sys("cpu", "emc");
if (IS_ERR(emc_clk)) {
clk_put(cpu_clk);
return PTR_ERR(emc_clk);
}
clk_prepare_enable(emc_clk);
clk_prepare_enable(cpu_clk);
@ -256,8 +238,6 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
{
cpufreq_frequency_table_cpuinfo(policy, freq_table);
clk_disable_unprepare(emc_clk);
clk_put(emc_clk);
clk_put(cpu_clk);
return 0;
}
@ -278,12 +258,32 @@ static struct cpufreq_driver tegra_cpufreq_driver = {
static int __init tegra_cpufreq_init(void)
{
cpu_clk = clk_get_sys(NULL, "cpu");
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
pll_x_clk = clk_get_sys(NULL, "pll_x");
if (IS_ERR(pll_x_clk))
return PTR_ERR(pll_x_clk);
pll_p_clk = clk_get_sys(NULL, "pll_p_cclk");
if (IS_ERR(pll_p_clk))
return PTR_ERR(pll_p_clk);
emc_clk = clk_get_sys("cpu", "emc");
if (IS_ERR(emc_clk)) {
clk_put(cpu_clk);
return PTR_ERR(emc_clk);
}
return cpufreq_register_driver(&tegra_cpufreq_driver);
}
static void __exit tegra_cpufreq_exit(void)
{
cpufreq_unregister_driver(&tegra_cpufreq_driver);
clk_put(emc_clk);
clk_put(cpu_clk);
}

View file

@ -24,6 +24,7 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/clockchips.h>
#include <linux/clk/tegra.h>
#include <asm/cpuidle.h>
#include <asm/proc-fns.h>
@ -32,7 +33,6 @@
#include "pm.h"
#include "sleep.h"
#include "tegra_cpu_car.h"
#ifdef CONFIG_PM_SLEEP
static int tegra30_idle_lp2(struct cpuidle_device *dev,
@ -121,9 +121,9 @@ static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
}
#endif
static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
static int tegra30_idle_lp2(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
bool entered_lp2 = false;

View file

@ -26,14 +26,14 @@
#include "flowctrl.h"
#include "iomap.h"
u8 flowctrl_offset_halt_cpu[] = {
static u8 flowctrl_offset_halt_cpu[] = {
FLOW_CTRL_HALT_CPU0_EVENTS,
FLOW_CTRL_HALT_CPU1_EVENTS,
FLOW_CTRL_HALT_CPU1_EVENTS + 8,
FLOW_CTRL_HALT_CPU1_EVENTS + 16,
};
u8 flowctrl_offset_cpu_csr[] = {
static u8 flowctrl_offset_cpu_csr[] = {
FLOW_CTRL_CPU0_CSR,
FLOW_CTRL_CPU1_CSR,
FLOW_CTRL_CPU1_CSR + 8,

View file

@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/tegra-soc.h>
#include "fuse.h"
#include "iomap.h"
@ -105,6 +106,11 @@ static void tegra_get_process_id(void)
tegra_core_process_id = (reg >> 12) & 3;
}
u32 tegra_read_chipid(void)
{
return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804);
}
void tegra_init_fuse(void)
{
u32 id;
@ -119,7 +125,7 @@ void tegra_init_fuse(void)
reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT);
tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT;
id = readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804);
id = tegra_read_chipid();
tegra_chip_id = (id >> 8) & 0xff;
switch (tegra_chip_id) {

View file

@ -1,22 +1,9 @@
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/cache.h>
#include <asm/asm-offsets.h>
#include <asm/hardware/cache-l2x0.h>
#include "flowctrl.h"
#include "iomap.h"
#include "reset.h"
#include "sleep.h"
#define APB_MISC_GP_HIDREV 0x804
#define PMC_SCRATCH41 0x140
#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
.section ".text.head", "ax"
__CPUINIT
/*
* Tegra specific entry point for secondary CPUs.
@ -61,7 +48,6 @@ ENTRY(v7_invalidate_l1)
mov pc, lr
ENDPROC(v7_invalidate_l1)
ENTRY(tegra_secondary_startup)
bl v7_invalidate_l1
/* Enable coresight */
@ -69,210 +55,3 @@ ENTRY(tegra_secondary_startup)
mcr p14, 0, r0, c7, c12, 6
b secondary_startup
ENDPROC(tegra_secondary_startup)
#ifdef CONFIG_PM_SLEEP
/*
* tegra_resume
*
* CPU boot vector when restarting the a CPU following
* an LP2 transition. Also branched to by LP0 and LP1 resume after
* re-enabling sdram.
*/
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?
bne cpu_resume @ no
#ifdef CONFIG_ARCH_TEGRA_3x_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)
beq 1f @ Yes
/* Clear the flow controller flags for this CPU. */
mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR
ldr r1, [r2]
/* Clear event & intr flag */
orr r1, r1, \
#FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps
bic r1, r1, r0
str r1, [r2]
1:
#endif
#ifdef CONFIG_HAVE_ARM_SCU
/* enable SCU */
mov32 r0, TEGRA_ARM_PERIF_BASE
ldr r1, [r0]
orr r1, r1, #1
str r1, [r0]
#endif
/* L2 cache resume & re-enable */
l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr
b cpu_resume
ENDPROC(tegra_resume)
#endif
#ifdef CONFIG_CACHE_L2X0
.globl l2x0_saved_regs_addr
l2x0_saved_regs_addr:
.long 0
#endif
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_start)
/*
* __tegra_cpu_reset_handler:
*
* Common handler for all CPU reset events.
*
* Register usage within the reset handler:
*
* R7 = CPU present (to the OS) mask
* R8 = CPU in LP1 state mask
* R9 = CPU in LP2 state mask
* R10 = CPU number
* R11 = CPU mask
* R12 = pointer to reset handler data
*
* NOTE: This code is copied to IRAM. All code and data accesses
* must be position-independent.
*/
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler)
cpsid aif, 0x13 @ SVC mode, interrupts disabled
mrc p15, 0, r10, c0, c0, 5 @ MPIDR
and r10, r10, #0x3 @ R10 = CPU number
mov r11, #1
mov r11, r11, lsl r10 @ R11 = CPU mask
adr r12, __tegra_cpu_reset_handler_data
#ifdef CONFIG_SMP
/* Does the OS know about this CPU? */
ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
tst r7, r11 @ if !present
bleq __die @ CPU not present (to OS)
#endif
#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)
bne 1f
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
mov32 r6, TEGRA_PMC_BASE
mov r0, #0
cmp r10, #0
strne r0, [r6, #PMC_SCRATCH41]
1:
#endif
/* Waking up from LP2? */
ldr r9, [r12, #RESET_DATA(MASK_LP2)]
tst r9, r11 @ if in_lp2
beq __is_not_lp2
ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
cmp lr, #0
bleq __die @ no LP2 startup handler
bx lr
__is_not_lp2:
#ifdef CONFIG_SMP
/*
* Can only be secondary boot (initial or hotplug) but CPU 0
* cannot be here.
*/
cmp r10, #0
bleq __die @ CPU0 cannot be here
ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
cmp lr, #0
bleq __die @ no secondary startup handler
bx lr
#endif
/*
* We don't know why the CPU reset. Just kill it.
* The LR register will contain the address we died at + 4.
*/
__die:
sub lr, lr, #4
mov32 r7, TEGRA_PMC_BASE
str lr, [r7, #PMC_SCRATCH41]
mov32 r7, TEGRA_CLK_RESET_BASE
/* 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)
bne 1f
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
mov32 r0, 0x1111
mov r1, r0, lsl r10
str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
#endif
1:
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
mov32 r6, TEGRA_FLOW_CTRL_BASE
cmp r10, #0
moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS
moveq r2, #FLOW_CTRL_CPU0_CSR
movne r1, r10, lsl #3
addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8)
addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8)
/* Clear CPU "event" and "interrupt" flags and power gate
it when halting but not before it is in the "WFI" state. */
ldr r0, [r6, +r2]
orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
orr r0, r0, #FLOW_CTRL_CSR_ENABLE
str r0, [r6, +r2]
/* Unconditionally halt this CPU */
mov r0, #FLOW_CTRL_WAITEVENT
str r0, [r6, +r1]
ldr r0, [r6, +r1] @ memory barrier
dsb
isb
wfi @ CPU should be power gated here
/* If the CPU didn't power gate above just kill it's clock. */
mov r0, r11, lsl #8
str r0, [r7, #348] @ CLK_CPU_CMPLX_SET
#endif
/* If the CPU still isn't dead, just spin here. */
b .
ENDPROC(__tegra_cpu_reset_handler)
.align L1_CACHE_SHIFT
.type __tegra_cpu_reset_handler_data, %object
.globl __tegra_cpu_reset_handler_data
__tegra_cpu_reset_handler_data:
.rept TEGRA_RESET_DATA_SIZE
.long 0
.endr
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_end)

View file

@ -10,15 +10,26 @@
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/clk/tegra.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include "sleep.h"
#include "tegra_cpu_car.h"
static void (*tegra_hotplug_shutdown)(void);
int tegra_cpu_kill(unsigned cpu)
{
cpu = cpu_logical_map(cpu);
/* Clock gate the CPU */
tegra_wait_cpu_in_reset(cpu);
tegra_disable_cpu_clock(cpu);
return 1;
}
/*
* platform-specific code to shutdown a CPU
*
@ -26,18 +37,12 @@ static void (*tegra_hotplug_shutdown)(void);
*/
void __ref tegra_cpu_die(unsigned int cpu)
{
cpu = cpu_logical_map(cpu);
/* Flush the L1 data cache. */
flush_cache_all();
/* Clean L1 data cache */
tegra_disable_clean_inv_dcache();
/* Shut down the current CPU. */
tegra_hotplug_shutdown();
/* Clock gate the CPU */
tegra_wait_cpu_in_reset(cpu);
tegra_disable_cpu_clock(cpu);
/* Should never return here. */
BUG();
}

View file

@ -1,44 +0,0 @@
/*
* arch/arm/mach-tegra/include/mach/clk.h
*
* Copyright (C) 2010 Google, Inc.
*
* Author:
* Erik Gilling <konkers@google.com>
*
* 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.
*
*/
#ifndef __MACH_CLK_H
#define __MACH_CLK_H
struct clk;
enum tegra_clk_ex_param {
TEGRA_CLK_VI_INP_SEL,
TEGRA_CLK_DTV_INVERT,
TEGRA_CLK_NAND_PAD_DIV2_ENB,
TEGRA_CLK_PLLD_CSI_OUT_ENB,
TEGRA_CLK_PLLD_DSI_OUT_ENB,
TEGRA_CLK_PLLD_MIPI_MUX_SEL,
};
void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
#ifndef CONFIG_COMMON_CLK
unsigned long clk_get_rate_all_locked(struct clk *c);
#endif
void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting);
#endif

View file

@ -33,11 +33,11 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk/tegra.h>
#include <asm/sizes.h>
#include <asm/mach/pci.h>
#include <mach/clk.h>
#include <mach/powergate.h>
#include "board.h"

View file

@ -19,23 +19,25 @@
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/clk/tegra.h>
#include <asm/cacheflush.h>
#include <asm/mach-types.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <mach/powergate.h>
#include "fuse.h"
#include "flowctrl.h"
#include "reset.h"
#include "tegra_cpu_car.h"
#include "common.h"
#include "iomap.h"
extern void tegra_secondary_startup(void);
static cpumask_t tegra_cpu_init_mask;
static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
#define EVP_CPU_RESET_VECTOR \
@ -50,6 +52,7 @@ static void __cpuinit tegra_secondary_init(unsigned int cpu)
*/
gic_secondary_init(0);
cpumask_set_cpu(cpu, &tegra_cpu_init_mask);
}
static int tegra20_power_up_cpu(unsigned int cpu)
@ -72,14 +75,42 @@ static int tegra30_power_up_cpu(unsigned int cpu)
if (pwrgateid < 0)
return pwrgateid;
/* If this is the first boot, toggle powergates directly. */
/*
* The power up sequence of cold boot CPU and warm boot CPU
* was different.
*
* For warm boot CPU that was resumed from CPU hotplug, the
* power will be resumed automatically after un-halting the
* flow controller of the warm boot CPU. We need to wait for
* the confirmaiton that the CPU is powered then removing
* 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()
* 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))
goto remove_clamps;
udelay(10);
} while (time_before(jiffies, timeout));
}
/*
* The power status of the cold boot CPU is power gated as
* default. To power up the cold boot CPU, the power should
* be un-gated by un-toggling the power gate register
* manually.
*/
if (!tegra_powergate_is_powered(pwrgateid)) {
ret = tegra_powergate_power_on(pwrgateid);
if (ret)
return ret;
/* Wait for the power to come up. */
timeout = jiffies + 10*HZ;
timeout = jiffies + msecs_to_jiffies(100);
while (tegra_powergate_is_powered(pwrgateid)) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
@ -87,6 +118,7 @@ static int tegra30_power_up_cpu(unsigned int cpu)
}
}
remove_clamps:
/* CPU partition is powered. Enable the CPU clock. */
tegra_enable_cpu_clock(cpu);
udelay(10);
@ -105,6 +137,8 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
{
int status;
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
@ -163,7 +197,9 @@ static void __init tegra_smp_init_cpus(void)
static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
{
tegra_cpu_reset_handler_init();
/* Always mark the boot CPU (CPU0) as initialized. */
cpumask_set_cpu(0, &tegra_cpu_init_mask);
scu_enable(scu_base);
}
@ -173,6 +209,7 @@ struct smp_operations tegra_smp_ops __initdata = {
.smp_secondary_init = tegra_secondary_init,
.smp_boot_secondary = tegra_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_kill = tegra_cpu_kill,
.cpu_die = tegra_cpu_die,
.cpu_disable = tegra_cpu_disable,
#endif

View file

@ -24,6 +24,7 @@
#include <linux/cpu_pm.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/clk/tegra.h>
#include <asm/smp_plat.h>
#include <asm/cacheflush.h>
@ -36,7 +37,6 @@
#include "reset.h"
#include "flowctrl.h"
#include "sleep.h"
#include "tegra_cpu_car.h"
#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
@ -148,7 +148,7 @@ static void suspend_cpu_complex(void)
save_cpu_arch_register();
}
void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
void tegra_clear_cpu_in_lp2(int phy_cpu_id)
{
u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
@ -160,7 +160,7 @@ void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
spin_unlock(&tegra_lp2_lock);
}
bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
bool tegra_set_cpu_in_lp2(int phy_cpu_id)
{
bool last_cpu = false;
cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;

View file

@ -26,8 +26,8 @@
#include <linux/io.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/clk/tegra.h>
#include <mach/clk.h>
#include <mach/powergate.h>
#include "fuse.h"

View file

@ -0,0 +1,239 @@
/*
* Copyright (c) 2012, 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/linkage.h>
#include <linux/init.h>
#include <asm/cache.h>
#include <asm/asm-offsets.h>
#include <asm/hardware/cache-l2x0.h>
#include "flowctrl.h"
#include "iomap.h"
#include "reset.h"
#include "sleep.h"
#define APB_MISC_GP_HIDREV 0x804
#define PMC_SCRATCH41 0x140
#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
#ifdef CONFIG_PM_SLEEP
/*
* tegra_resume
*
* CPU boot vector when restarting the a CPU following
* an LP2 transition. Also branched to by LP0 and LP1 resume after
* re-enabling sdram.
*/
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?
bne cpu_resume @ no
#ifdef CONFIG_ARCH_TEGRA_3x_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)
beq 1f @ Yes
/* Clear the flow controller flags for this CPU. */
mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR
ldr r1, [r2]
/* Clear event & intr flag */
orr r1, r1, \
#FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps
bic r1, r1, r0
str r1, [r2]
1:
#endif
#ifdef CONFIG_HAVE_ARM_SCU
/* enable SCU */
mov32 r0, TEGRA_ARM_PERIF_BASE
ldr r1, [r0]
orr r1, r1, #1
str r1, [r0]
#endif
/* L2 cache resume & re-enable */
l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr
b cpu_resume
ENDPROC(tegra_resume)
#endif
#ifdef CONFIG_CACHE_L2X0
.globl l2x0_saved_regs_addr
l2x0_saved_regs_addr:
.long 0
#endif
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_start)
/*
* __tegra_cpu_reset_handler:
*
* Common handler for all CPU reset events.
*
* Register usage within the reset handler:
*
* R7 = CPU present (to the OS) mask
* R8 = CPU in LP1 state mask
* R9 = CPU in LP2 state mask
* R10 = CPU number
* R11 = CPU mask
* R12 = pointer to reset handler data
*
* NOTE: This code is copied to IRAM. All code and data accesses
* must be position-independent.
*/
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler)
cpsid aif, 0x13 @ SVC mode, interrupts disabled
mrc p15, 0, r10, c0, c0, 5 @ MPIDR
and r10, r10, #0x3 @ R10 = CPU number
mov r11, #1
mov r11, r11, lsl r10 @ R11 = CPU mask
adr r12, __tegra_cpu_reset_handler_data
#ifdef CONFIG_SMP
/* Does the OS know about this CPU? */
ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
tst r7, r11 @ if !present
bleq __die @ CPU not present (to OS)
#endif
#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)
bne 1f
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
mov32 r6, TEGRA_PMC_BASE
mov r0, #0
cmp r10, #0
strne r0, [r6, #PMC_SCRATCH41]
1:
#endif
/* Waking up from LP2? */
ldr r9, [r12, #RESET_DATA(MASK_LP2)]
tst r9, r11 @ if in_lp2
beq __is_not_lp2
ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
cmp lr, #0
bleq __die @ no LP2 startup handler
bx lr
__is_not_lp2:
#ifdef CONFIG_SMP
/*
* Can only be secondary boot (initial or hotplug) but CPU 0
* cannot be here.
*/
cmp r10, #0
bleq __die @ CPU0 cannot be here
ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
cmp lr, #0
bleq __die @ no secondary startup handler
bx lr
#endif
/*
* We don't know why the CPU reset. Just kill it.
* The LR register will contain the address we died at + 4.
*/
__die:
sub lr, lr, #4
mov32 r7, TEGRA_PMC_BASE
str lr, [r7, #PMC_SCRATCH41]
mov32 r7, TEGRA_CLK_RESET_BASE
/* 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)
bne 1f
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
mov32 r0, 0x1111
mov r1, r0, lsl r10
str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
#endif
1:
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
mov32 r6, TEGRA_FLOW_CTRL_BASE
cmp r10, #0
moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS
moveq r2, #FLOW_CTRL_CPU0_CSR
movne r1, r10, lsl #3
addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8)
addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8)
/* Clear CPU "event" and "interrupt" flags and power gate
it when halting but not before it is in the "WFI" state. */
ldr r0, [r6, +r2]
orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
orr r0, r0, #FLOW_CTRL_CSR_ENABLE
str r0, [r6, +r2]
/* Unconditionally halt this CPU */
mov r0, #FLOW_CTRL_WAITEVENT
str r0, [r6, +r1]
ldr r0, [r6, +r1] @ memory barrier
dsb
isb
wfi @ CPU should be power gated here
/* If the CPU didn't power gate above just kill it's clock. */
mov r0, r11, lsl #8
str r0, [r7, #348] @ CLK_CPU_CMPLX_SET
#endif
/* If the CPU still isn't dead, just spin here. */
b .
ENDPROC(__tegra_cpu_reset_handler)
.align L1_CACHE_SHIFT
.type __tegra_cpu_reset_handler_data, %object
.globl __tegra_cpu_reset_handler_data
__tegra_cpu_reset_handler_data:
.rept TEGRA_RESET_DATA_SIZE
.long 0
.endr
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_end)

View file

@ -75,7 +75,7 @@ void __init tegra_cpu_reset_handler_init(void)
#ifdef CONFIG_SMP
__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
*((u32 *)cpu_present_mask);
*((u32 *)cpu_possible_mask);
__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
virt_to_phys((void *)tegra_secondary_startup);
#endif

View file

@ -33,9 +33,6 @@
* should never return
*/
ENTRY(tegra20_hotplug_shutdown)
/* Turn off SMP coherency */
exit_smp r4, r5
/* Put this CPU down */
cpu_id r0
bl tegra20_cpu_shutdown

View file

@ -32,9 +32,6 @@
* Should never return.
*/
ENTRY(tegra30_hotplug_shutdown)
/* Turn off SMP coherency */
exit_smp r4, r5
/* Powergate this CPU */
mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
bl tegra30_cpu_shutdown

View file

@ -34,7 +34,7 @@
#include "flowctrl.h"
#include "sleep.h"
#ifdef CONFIG_PM_SLEEP
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
/*
* tegra_disable_clean_inv_dcache
*
@ -60,7 +60,9 @@ ENTRY(tegra_disable_clean_inv_dcache)
ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
ENDPROC(tegra_disable_clean_inv_dcache)
#endif
#ifdef CONFIG_PM_SLEEP
/*
* tegra_sleep_cpu_finish(unsigned long v2p)
*

View file

@ -106,6 +106,7 @@
#else
void tegra_resume(void);
int tegra_sleep_cpu_finish(unsigned long);
void tegra_disable_clean_inv_dcache(void);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);

File diff suppressed because it is too large Load diff

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2012, 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/>.
*/
#ifndef __MACH_TEGRA20_CLOCK_H
#define __MACH_TEGRA20_CLOCK_H
extern struct clk_ops tegra_clk_32k_ops;
extern struct clk_ops tegra_pll_ops;
extern struct clk_ops tegra_clk_m_ops;
extern struct clk_ops tegra_pll_div_ops;
extern struct clk_ops tegra_pllx_ops;
extern struct clk_ops tegra_plle_ops;
extern struct clk_ops tegra_clk_double_ops;
extern struct clk_ops tegra_cdev_clk_ops;
extern struct clk_ops tegra_audio_sync_clk_ops;
extern struct clk_ops tegra_super_ops;
extern struct clk_ops tegra_cpu_ops;
extern struct clk_ops tegra_twd_ops;
extern struct clk_ops tegra_cop_ops;
extern struct clk_ops tegra_bus_ops;
extern struct clk_ops tegra_blink_clk_ops;
extern struct clk_ops tegra_emc_clk_ops;
extern struct clk_ops tegra_periph_clk_ops;
extern struct clk_ops tegra_clk_shared_bus_ops;
void tegra2_periph_clk_reset(struct clk_hw *hw, bool assert);
void tegra2_cop_clk_reset(struct clk_hw *hw, bool assert);
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2012, 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/>.
*/
#ifndef __MACH_TEGRA30_CLOCK_H
#define __MACH_TEGRA30_CLOCK_H
extern struct clk_ops tegra30_clk_32k_ops;
extern struct clk_ops tegra30_clk_m_ops;
extern struct clk_ops tegra_clk_m_div_ops;
extern struct clk_ops tegra_pll_ref_ops;
extern struct clk_ops tegra30_pll_ops;
extern struct clk_ops tegra30_pll_div_ops;
extern struct clk_ops tegra_plld_ops;
extern struct clk_ops tegra30_plle_ops;
extern struct clk_ops tegra_cml_clk_ops;
extern struct clk_ops tegra_pciex_clk_ops;
extern struct clk_ops tegra_sync_source_ops;
extern struct clk_ops tegra30_audio_sync_clk_ops;
extern struct clk_ops tegra30_clk_double_ops;
extern struct clk_ops tegra_clk_out_ops;
extern struct clk_ops tegra30_super_ops;
extern struct clk_ops tegra30_blink_clk_ops;
extern struct clk_ops tegra30_twd_ops;
extern struct clk_ops tegra30_bus_ops;
extern struct clk_ops tegra30_periph_clk_ops;
extern struct clk_ops tegra30_dsib_clk_ops;
extern struct clk_ops tegra_nand_clk_ops;
extern struct clk_ops tegra_vi_clk_ops;
extern struct clk_ops tegra_dtv_clk_ops;
extern struct clk_ops tegra_clk_shared_bus_ops;
int tegra30_plld_clk_cfg_ex(struct clk_hw *hw,
enum tegra_clk_ex_param p, u32 setting);
void tegra30_periph_clk_reset(struct clk_hw *hw, bool assert);
int tegra30_vi_clk_cfg_ex(struct clk_hw *hw,
enum tegra_clk_ex_param p, u32 setting);
int tegra30_nand_clk_cfg_ex(struct clk_hw *hw,
enum tegra_clk_ex_param p, u32 setting);
int tegra30_dtv_clk_cfg_ex(struct clk_hw *hw,
enum tegra_clk_ex_param p, u32 setting);
#endif

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o

View file

@ -0,0 +1,11 @@
obj-y += clk.o
obj-y += clk-audio-sync.o
obj-y += clk-divider.o
obj-y += clk-periph.o
obj-y += clk-periph-gate.o
obj-y += clk-pll.o
obj-y += clk-pll-out.o
obj-y += clk-super.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2012, 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-provider.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "clk.h"
static unsigned long clk_sync_source_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
return sync->rate;
}
static long clk_sync_source_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
if (rate > sync->max_rate)
return -EINVAL;
else
return rate;
}
static int clk_sync_source_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
sync->rate = rate;
return 0;
}
const struct clk_ops tegra_clk_sync_source_ops = {
.round_rate = clk_sync_source_round_rate,
.set_rate = clk_sync_source_set_rate,
.recalc_rate = clk_sync_source_recalc_rate,
};
struct clk *tegra_clk_register_sync_source(const char *name,
unsigned long rate, unsigned long max_rate)
{
struct tegra_clk_sync_source *sync;
struct clk_init_data init;
struct clk *clk;
sync = kzalloc(sizeof(*sync), GFP_KERNEL);
if (!sync) {
pr_err("%s: could not allocate sync source clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
sync->rate = rate;
sync->max_rate = max_rate;
init.ops = &tegra_clk_sync_source_ops;
init.name = name;
init.flags = CLK_IS_ROOT;
init.parent_names = NULL;
init.num_parents = 0;
/* Data in .init is copied by clk_register(), so stack variable OK */
sync->hw.init = &init;
clk = clk_register(NULL, &sync->hw);
if (IS_ERR(clk))
kfree(sync);
return clk;
}

View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2012, 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/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include "clk.h"
#define pll_out_override(p) (BIT((p->shift - 6)))
#define div_mask(d) ((1 << (d->width)) - 1)
#define get_mul(d) (1 << d->frac_width)
#define get_max_div(d) div_mask(d)
#define PERIPH_CLK_UART_DIV_ENB BIT(24)
static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
unsigned long parent_rate)
{
s64 divider_ux1 = parent_rate;
u8 flags = divider->flags;
int mul;
if (!rate)
return 0;
mul = get_mul(divider);
if (!(flags & TEGRA_DIVIDER_INT))
divider_ux1 *= mul;
if (flags & TEGRA_DIVIDER_ROUND_UP)
divider_ux1 += rate - 1;
do_div(divider_ux1, rate);
if (flags & TEGRA_DIVIDER_INT)
divider_ux1 *= mul;
divider_ux1 -= mul;
if (divider_ux1 < 0)
return 0;
if (divider_ux1 > get_max_div(divider))
return -EINVAL;
return divider_ux1;
}
static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
u32 reg;
int div, mul;
u64 rate = parent_rate;
reg = readl_relaxed(divider->reg) >> divider->shift;
div = reg & div_mask(divider);
mul = get_mul(divider);
div += mul;
rate *= mul;
rate += div - 1;
do_div(rate, div);
return rate;
}
static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
int div, mul;
unsigned long output_rate = *prate;
if (!rate)
return output_rate;
div = get_div(divider, rate, output_rate);
if (div < 0)
return *prate;
mul = get_mul(divider);
return DIV_ROUND_UP(output_rate * mul, div + mul);
}
static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
int div;
unsigned long flags = 0;
u32 val;
div = get_div(divider, rate, parent_rate);
if (div < 0)
return div;
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
val = readl_relaxed(divider->reg);
val &= ~(div_mask(divider) << divider->shift);
val |= div << divider->shift;
if (divider->flags & TEGRA_DIVIDER_UART) {
if (div)
val |= PERIPH_CLK_UART_DIV_ENB;
else
val &= ~PERIPH_CLK_UART_DIV_ENB;
}
if (divider->flags & TEGRA_DIVIDER_FIXED)
val |= pll_out_override(divider);
writel_relaxed(val, divider->reg);
if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags);
return 0;
}
const struct clk_ops tegra_clk_frac_div_ops = {
.recalc_rate = clk_frac_div_recalc_rate,
.set_rate = clk_frac_div_set_rate,
.round_rate = clk_frac_div_round_rate,
};
struct clk *tegra_clk_register_divider(const char *name,
const char *parent_name, void __iomem *reg,
unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
u8 frac_width, spinlock_t *lock)
{
struct tegra_clk_frac_div *divider;
struct clk *clk;
struct clk_init_data init;
divider = kzalloc(sizeof(*divider), GFP_KERNEL);
if (!divider) {
pr_err("%s: could not allocate fractional divider clk\n",
__func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &tegra_clk_frac_div_ops;
init.flags = flags;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
divider->reg = reg;
divider->shift = shift;
divider->width = width;
divider->frac_width = frac_width;
divider->lock = lock;
divider->flags = clk_divider_flags;
/* Data in .init is copied by clk_register(), so stack variable OK */
divider->hw.init = &init;
clk = clk_register(NULL, &divider->hw);
if (IS_ERR(clk))
kfree(divider);
return clk;
}

View file

@ -0,0 +1,179 @@
/*
* Copyright (c) 2012, 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/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/tegra-soc.h>
#include "clk.h"
static DEFINE_SPINLOCK(periph_ref_lock);
/* Macros to assist peripheral gate clock */
#define read_enb(gate) \
readl_relaxed(gate->clk_base + (gate->regs->enb_reg))
#define write_enb_set(val, gate) \
writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg))
#define write_enb_clr(val, gate) \
writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg))
#define read_rst(gate) \
readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
#define write_rst_set(val, gate) \
writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg))
#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))
/* Peripheral gate clock ops */
static int clk_periph_is_enabled(struct clk_hw *hw)
{
struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
int state = 1;
if (!(read_enb(gate) & periph_clk_to_bit(gate)))
state = 0;
if (!(gate->flags & TEGRA_PERIPH_NO_RESET))
if (read_rst(gate) & periph_clk_to_bit(gate))
state = 0;
return state;
}
static int clk_periph_enable(struct clk_hw *hw)
{
struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
unsigned long flags = 0;
spin_lock_irqsave(&periph_ref_lock, flags);
gate->enable_refcnt[gate->clk_num]++;
if (gate->enable_refcnt[gate->clk_num] > 1) {
spin_unlock_irqrestore(&periph_ref_lock, flags);
return 0;
}
write_enb_set(periph_clk_to_bit(gate), gate);
udelay(2);
if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
!(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
if (read_rst(gate) & periph_clk_to_bit(gate)) {
udelay(5); /* reset propogation delay */
write_rst_clr(periph_clk_to_bit(gate), gate);
}
}
spin_unlock_irqrestore(&periph_ref_lock, flags);
return 0;
}
static void clk_periph_disable(struct clk_hw *hw)
{
struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
unsigned long flags = 0;
spin_lock_irqsave(&periph_ref_lock, flags);
gate->enable_refcnt[gate->clk_num]--;
if (gate->enable_refcnt[gate->clk_num] > 0) {
spin_unlock_irqrestore(&periph_ref_lock, flags);
return;
}
/*
* If peripheral is in the APB bus then read the APB bus to
* flush the write operation in apb bus. This will avoid the
* peripheral access after disabling clock
*/
if (gate->flags & TEGRA_PERIPH_ON_APB)
tegra_read_chipid();
write_enb_clr(periph_clk_to_bit(gate), gate);
spin_unlock_irqrestore(&periph_ref_lock, flags);
}
void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert)
{
if (gate->flags & TEGRA_PERIPH_NO_RESET)
return;
if (assert) {
/*
* If peripheral is in the APB bus then read the APB bus to
* flush the write operation in apb bus. This will avoid the
* peripheral access after disabling clock
*/
if (gate->flags & TEGRA_PERIPH_ON_APB)
tegra_read_chipid();
write_rst_set(periph_clk_to_bit(gate), gate);
} else {
write_rst_clr(periph_clk_to_bit(gate), gate);
}
}
const struct clk_ops tegra_clk_periph_gate_ops = {
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
};
struct clk *tegra_clk_register_periph_gate(const char *name,
const char *parent_name, u8 gate_flags, void __iomem *clk_base,
unsigned long flags, int clk_num,
struct tegra_clk_periph_regs *pregs, int *enable_refcnt)
{
struct tegra_clk_periph_gate *gate;
struct clk *clk;
struct clk_init_data init;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
pr_err("%s: could not allocate periph gate clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.flags = flags;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.ops = &tegra_clk_periph_gate_ops;
gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;
gate->clk_base = clk_base;
gate->clk_num = clk_num;
gate->flags = gate_flags;
gate->enable_refcnt = enable_refcnt;
gate->regs = pregs;
/* Data in .init is copied by clk_register(), so stack variable OK */
gate->hw.init = &init;
clk = clk_register(NULL, &gate->hw);
if (IS_ERR(clk))
kfree(gate);
return clk;
}

View file

@ -0,0 +1,218 @@
/*
* Copyright (c) 2012, 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/slab.h>
#include <linux/err.h>
#include "clk.h"
static u8 clk_periph_get_parent(struct clk_hw *hw)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *mux_ops = periph->mux_ops;
struct clk_hw *mux_hw = &periph->mux.hw;
mux_hw->clk = hw->clk;
return mux_ops->get_parent(mux_hw);
}
static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *mux_ops = periph->mux_ops;
struct clk_hw *mux_hw = &periph->mux.hw;
mux_hw->clk = hw->clk;
return mux_ops->set_parent(mux_hw, index);
}
static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *div_ops = periph->div_ops;
struct clk_hw *div_hw = &periph->divider.hw;
div_hw->clk = hw->clk;
return div_ops->recalc_rate(div_hw, parent_rate);
}
static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *div_ops = periph->div_ops;
struct clk_hw *div_hw = &periph->divider.hw;
div_hw->clk = hw->clk;
return div_ops->round_rate(div_hw, rate, prate);
}
static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *div_ops = periph->div_ops;
struct clk_hw *div_hw = &periph->divider.hw;
div_hw->clk = hw->clk;
return div_ops->set_rate(div_hw, rate, parent_rate);
}
static int clk_periph_is_enabled(struct clk_hw *hw)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *gate_ops = periph->gate_ops;
struct clk_hw *gate_hw = &periph->gate.hw;
gate_hw->clk = hw->clk;
return gate_ops->is_enabled(gate_hw);
}
static int clk_periph_enable(struct clk_hw *hw)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *gate_ops = periph->gate_ops;
struct clk_hw *gate_hw = &periph->gate.hw;
gate_hw->clk = hw->clk;
return gate_ops->enable(gate_hw);
}
static void clk_periph_disable(struct clk_hw *hw)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *gate_ops = periph->gate_ops;
struct clk_hw *gate_hw = &periph->gate.hw;
gate_ops->disable(gate_hw);
}
void tegra_periph_reset_deassert(struct clk *c)
{
struct clk_hw *hw = __clk_get_hw(c);
struct tegra_clk_periph *periph = to_clk_periph(hw);
struct tegra_clk_periph_gate *gate;
if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
gate = to_clk_periph_gate(hw);
if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
WARN_ON(1);
return;
}
} else {
gate = &periph->gate;
}
tegra_periph_reset(gate, 0);
}
void tegra_periph_reset_assert(struct clk *c)
{
struct clk_hw *hw = __clk_get_hw(c);
struct tegra_clk_periph *periph = to_clk_periph(hw);
struct tegra_clk_periph_gate *gate;
if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
gate = to_clk_periph_gate(hw);
if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
WARN_ON(1);
return;
}
} else {
gate = &periph->gate;
}
tegra_periph_reset(gate, 1);
}
const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.set_rate = clk_periph_set_rate,
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
};
const struct clk_ops tegra_clk_periph_nodiv_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
};
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)
{
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.parent_names = parent_names;
init.num_parents = num_parents;
/* Data in .init is copied by clk_register(), so stack variable OK */
periph->hw.init = &init;
periph->magic = TEGRA_CLK_PERIPH_MAGIC;
periph->mux.reg = clk_base + offset;
periph->divider.reg = div ? (clk_base + offset) : NULL;
periph->gate.clk_base = clk_base;
clk = clk_register(NULL, &periph->hw);
if (IS_ERR(clk))
return clk;
periph->mux.hw.clk = clk;
periph->divider.hw.clk = div ? clk : NULL;
periph->gate.hw.clk = clk;
return clk;
}
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)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, true);
}
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)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, false);
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2012, 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/io.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include "clk.h"
#define pll_out_enb(p) (BIT(p->enb_bit_idx))
#define pll_out_rst(p) (BIT(p->rst_bit_idx))
static int clk_pll_out_is_enabled(struct clk_hw *hw)
{
struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
u32 val = readl_relaxed(pll_out->reg);
int state;
state = (val & pll_out_enb(pll_out)) ? 1 : 0;
if (!(val & (pll_out_rst(pll_out))))
state = 0;
return state;
}
static int clk_pll_out_enable(struct clk_hw *hw)
{
struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
unsigned long flags = 0;
u32 val;
if (pll_out->lock)
spin_lock_irqsave(pll_out->lock, flags);
val = readl_relaxed(pll_out->reg);
val |= (pll_out_enb(pll_out) | pll_out_rst(pll_out));
writel_relaxed(val, pll_out->reg);
udelay(2);
if (pll_out->lock)
spin_unlock_irqrestore(pll_out->lock, flags);
return 0;
}
static void clk_pll_out_disable(struct clk_hw *hw)
{
struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
unsigned long flags = 0;
u32 val;
if (pll_out->lock)
spin_lock_irqsave(pll_out->lock, flags);
val = readl_relaxed(pll_out->reg);
val &= ~(pll_out_enb(pll_out) | pll_out_rst(pll_out));
writel_relaxed(val, pll_out->reg);
udelay(2);
if (pll_out->lock)
spin_unlock_irqrestore(pll_out->lock, flags);
}
const struct clk_ops tegra_clk_pll_out_ops = {
.is_enabled = clk_pll_out_is_enabled,
.enable = clk_pll_out_enable,
.disable = clk_pll_out_disable,
};
struct clk *tegra_clk_register_pll_out(const char *name,
const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
u8 rst_bit_idx, unsigned long flags, u8 pll_out_flags,
spinlock_t *lock)
{
struct tegra_clk_pll_out *pll_out;
struct clk *clk;
struct clk_init_data init;
pll_out = kzalloc(sizeof(*pll_out), GFP_KERNEL);
if (!pll_out)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &tegra_clk_pll_out_ops;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
init.flags = flags;
pll_out->reg = reg;
pll_out->enb_bit_idx = enb_bit_idx;
pll_out->rst_bit_idx = rst_bit_idx;
pll_out->flags = pll_out_flags;
pll_out->lock = lock;
/* Data in .init is copied by clk_register(), so stack variable OK */
pll_out->hw.init = &init;
clk = clk_register(NULL, &pll_out->hw);
if (IS_ERR(clk))
kfree(pll_out);
return clk;
}

648
drivers/clk/tegra/clk-pll.c Normal file
View file

@ -0,0 +1,648 @@
/*
* Copyright (c) 2012, 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/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include "clk.h"
#define PLL_BASE_BYPASS BIT(31)
#define PLL_BASE_ENABLE BIT(30)
#define PLL_BASE_REF_ENABLE BIT(29)
#define PLL_BASE_OVERRIDE BIT(28)
#define PLL_BASE_DIVP_SHIFT 20
#define PLL_BASE_DIVP_WIDTH 3
#define PLL_BASE_DIVN_SHIFT 8
#define PLL_BASE_DIVN_WIDTH 10
#define PLL_BASE_DIVM_SHIFT 0
#define PLL_BASE_DIVM_WIDTH 5
#define PLLU_POST_DIVP_MASK 0x1
#define PLL_MISC_DCCON_SHIFT 20
#define PLL_MISC_CPCON_SHIFT 8
#define PLL_MISC_CPCON_WIDTH 4
#define PLL_MISC_CPCON_MASK ((1 << PLL_MISC_CPCON_WIDTH) - 1)
#define PLL_MISC_LFCON_SHIFT 4
#define PLL_MISC_LFCON_WIDTH 4
#define PLL_MISC_LFCON_MASK ((1 << PLL_MISC_LFCON_WIDTH) - 1)
#define PLL_MISC_VCOCON_SHIFT 0
#define PLL_MISC_VCOCON_WIDTH 4
#define PLL_MISC_VCOCON_MASK ((1 << PLL_MISC_VCOCON_WIDTH) - 1)
#define OUT_OF_TABLE_CPCON 8
#define PMC_PLLP_WB0_OVERRIDE 0xf8
#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE BIT(12)
#define PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE BIT(11)
#define PLL_POST_LOCK_DELAY 50
#define PLLDU_LFCON_SET_DIVN 600
#define PLLE_BASE_DIVCML_SHIFT 24
#define PLLE_BASE_DIVCML_WIDTH 4
#define PLLE_BASE_DIVP_SHIFT 16
#define PLLE_BASE_DIVP_WIDTH 7
#define PLLE_BASE_DIVN_SHIFT 8
#define PLLE_BASE_DIVN_WIDTH 8
#define PLLE_BASE_DIVM_SHIFT 0
#define PLLE_BASE_DIVM_WIDTH 8
#define PLLE_MISC_SETUP_BASE_SHIFT 16
#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT)
#define PLLE_MISC_LOCK_ENABLE BIT(9)
#define PLLE_MISC_READY BIT(15)
#define PLLE_MISC_SETUP_EX_SHIFT 2
#define PLLE_MISC_SETUP_EX_MASK (3 << PLLE_MISC_SETUP_EX_SHIFT)
#define PLLE_MISC_SETUP_MASK (PLLE_MISC_SETUP_BASE_MASK | \
PLLE_MISC_SETUP_EX_MASK)
#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT)
#define PLLE_SS_CTRL 0x68
#define PLLE_SS_DISABLE (7 << 10)
#define PMC_SATA_PWRGT 0x1ac
#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4)
#define pll_readl(offset, p) readl_relaxed(p->clk_base + offset)
#define pll_readl_base(p) pll_readl(p->params->base_reg, p)
#define pll_readl_misc(p) pll_readl(p->params->misc_reg, p)
#define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset)
#define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p)
#define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p)
#define mask(w) ((1 << (w)) - 1)
#define divm_mask(p) mask(p->divm_width)
#define divn_mask(p) mask(p->divn_width)
#define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \
mask(p->divp_width))
#define divm_max(p) (divm_mask(p))
#define divn_max(p) (divn_mask(p))
#define divp_max(p) (1 << (divp_mask(p)))
static void clk_pll_enable_lock(struct tegra_clk_pll *pll)
{
u32 val;
if (!(pll->flags & TEGRA_PLL_USE_LOCK))
return;
val = pll_readl_misc(pll);
val |= BIT(pll->params->lock_enable_bit_idx);
pll_writel_misc(val, pll);
}
static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll,
void __iomem *lock_addr, u32 lock_bit_idx)
{
int i;
u32 val;
if (!(pll->flags & TEGRA_PLL_USE_LOCK)) {
udelay(pll->params->lock_delay);
return 0;
}
for (i = 0; i < pll->params->lock_delay; i++) {
val = readl_relaxed(lock_addr);
if (val & BIT(lock_bit_idx)) {
udelay(PLL_POST_LOCK_DELAY);
return 0;
}
udelay(2); /* timeout = 2 * lock time */
}
pr_err("%s: Timed out waiting for pll %s lock\n", __func__,
__clk_get_name(pll->hw.clk));
return -1;
}
static int clk_pll_is_enabled(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
u32 val;
if (pll->flags & TEGRA_PLLM) {
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)
return val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE ? 1 : 0;
}
val = pll_readl_base(pll);
return val & PLL_BASE_ENABLE ? 1 : 0;
}
static int _clk_pll_enable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
u32 val;
clk_pll_enable_lock(pll);
val = pll_readl_base(pll);
val &= ~PLL_BASE_BYPASS;
val |= PLL_BASE_ENABLE;
pll_writel_base(val, pll);
if (pll->flags & TEGRA_PLLM) {
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
}
clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg,
pll->params->lock_bit_idx);
return 0;
}
static void _clk_pll_disable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
u32 val;
val = pll_readl_base(pll);
val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
pll_writel_base(val, pll);
if (pll->flags & TEGRA_PLLM) {
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
}
}
static int clk_pll_enable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
unsigned long flags = 0;
int ret;
if (pll->lock)
spin_lock_irqsave(pll->lock, flags);
ret = _clk_pll_enable(hw);
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
return ret;
}
static void clk_pll_disable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
unsigned long flags = 0;
if (pll->lock)
spin_lock_irqsave(pll->lock, flags);
_clk_pll_disable(hw);
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
}
static int _get_table_rate(struct clk_hw *hw,
struct tegra_clk_pll_freq_table *cfg,
unsigned long rate, unsigned long parent_rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
struct tegra_clk_pll_freq_table *sel;
for (sel = pll->freq_table; sel->input_rate != 0; sel++)
if (sel->input_rate == parent_rate &&
sel->output_rate == rate)
break;
if (sel->input_rate == 0)
return -EINVAL;
BUG_ON(sel->p < 1);
cfg->input_rate = sel->input_rate;
cfg->output_rate = sel->output_rate;
cfg->m = sel->m;
cfg->n = sel->n;
cfg->p = sel->p;
cfg->cpcon = sel->cpcon;
return 0;
}
static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
unsigned long rate, unsigned long parent_rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
unsigned long cfreq;
u32 p_div = 0;
switch (parent_rate) {
case 12000000:
case 26000000:
cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
break;
case 13000000:
cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
break;
case 16800000:
case 19200000:
cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
break;
case 9600000:
case 28800000:
/*
* PLL_P_OUT1 rate is not listed in PLLA table
*/
cfreq = parent_rate/(parent_rate/1000000);
break;
default:
pr_err("%s Unexpected reference rate %lu\n",
__func__, parent_rate);
BUG();
}
/* Raise VCO to guarantee 0.5% accuracy */
for (cfg->output_rate = rate; cfg->output_rate < 200 * cfreq;
cfg->output_rate <<= 1)
p_div++;
cfg->p = 1 << p_div;
cfg->m = parent_rate / cfreq;
cfg->n = cfg->output_rate / cfreq;
cfg->cpcon = OUT_OF_TABLE_CPCON;
if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) ||
cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) {
pr_err("%s: Failed to set %s rate %lu\n",
__func__, __clk_get_name(hw->clk), rate);
return -EINVAL;
}
return 0;
}
static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
unsigned long rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
unsigned long flags = 0;
u32 divp, val, old_base;
int state;
divp = __ffs(cfg->p);
if (pll->flags & TEGRA_PLLU)
divp ^= 1;
if (pll->lock)
spin_lock_irqsave(pll->lock, flags);
old_base = val = pll_readl_base(pll);
val &= ~((divm_mask(pll) << pll->divm_shift) |
(divn_mask(pll) << pll->divn_shift) |
(divp_mask(pll) << pll->divp_shift));
val |= ((cfg->m << pll->divm_shift) |
(cfg->n << pll->divn_shift) |
(divp << pll->divp_shift));
if (val == old_base) {
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
return 0;
}
state = clk_pll_is_enabled(hw);
if (state) {
_clk_pll_disable(hw);
val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
}
pll_writel_base(val, pll);
if (pll->flags & TEGRA_PLL_HAS_CPCON) {
val = pll_readl_misc(pll);
val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT);
val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT;
if (pll->flags & TEGRA_PLL_SET_LFCON) {
val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT);
if (cfg->n >= PLLDU_LFCON_SET_DIVN)
val |= 0x1 << PLL_MISC_LFCON_SHIFT;
} else if (pll->flags & TEGRA_PLL_SET_DCCON) {
val &= ~(0x1 << PLL_MISC_DCCON_SHIFT);
if (rate >= (pll->params->vco_max >> 1))
val |= 0x1 << PLL_MISC_DCCON_SHIFT;
}
pll_writel_misc(val, pll);
}
if (pll->lock)
spin_unlock_irqrestore(pll->lock, flags);
if (state)
clk_pll_enable(hw);
return 0;
}
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
struct tegra_clk_pll_freq_table cfg;
if (pll->flags & TEGRA_PLL_FIXED) {
if (rate != pll->fixed_rate) {
pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
__func__, __clk_get_name(hw->clk),
pll->fixed_rate, rate);
return -EINVAL;
}
return 0;
}
if (_get_table_rate(hw, &cfg, rate, parent_rate) &&
_calc_rate(hw, &cfg, rate, parent_rate))
return -EINVAL;
return _program_pll(hw, &cfg, rate);
}
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
struct tegra_clk_pll_freq_table cfg;
u64 output_rate = *prate;
if (pll->flags & TEGRA_PLL_FIXED)
return pll->fixed_rate;
/* PLLM is used for memory; we do not change rate */
if (pll->flags & TEGRA_PLLM)
return __clk_get_rate(hw->clk);
if (_get_table_rate(hw, &cfg, rate, *prate) &&
_calc_rate(hw, &cfg, rate, *prate))
return -EINVAL;
output_rate *= cfg.n;
do_div(output_rate, cfg.m * cfg.p);
return output_rate;
}
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
u32 val = pll_readl_base(pll);
u32 divn = 0, divm = 0, divp = 0;
u64 rate = parent_rate;
if (val & PLL_BASE_BYPASS)
return parent_rate;
if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) {
struct tegra_clk_pll_freq_table sel;
if (_get_table_rate(hw, &sel, pll->fixed_rate, parent_rate)) {
pr_err("Clock %s has unknown fixed frequency\n",
__clk_get_name(hw->clk));
BUG();
}
return pll->fixed_rate;
}
divp = (val >> pll->divp_shift) & (divp_mask(pll));
if (pll->flags & TEGRA_PLLU)
divp ^= 1;
divn = (val >> pll->divn_shift) & (divn_mask(pll));
divm = (val >> pll->divm_shift) & (divm_mask(pll));
divm *= (1 << divp);
rate *= divn;
do_div(rate, divm);
return rate;
}
static int clk_plle_training(struct tegra_clk_pll *pll)
{
u32 val;
unsigned long timeout;
if (!pll->pmc)
return -ENOSYS;
/*
* PLLE is already disabled, and setup cleared;
* create falling edge on PLLE IDDQ input.
*/
val = readl(pll->pmc + PMC_SATA_PWRGT);
val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
writel(val, pll->pmc + PMC_SATA_PWRGT);
val = readl(pll->pmc + PMC_SATA_PWRGT);
val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
writel(val, pll->pmc + PMC_SATA_PWRGT);
val = readl(pll->pmc + PMC_SATA_PWRGT);
val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
writel(val, pll->pmc + PMC_SATA_PWRGT);
val = pll_readl_misc(pll);
timeout = jiffies + msecs_to_jiffies(100);
while (1) {
val = pll_readl_misc(pll);
if (val & PLLE_MISC_READY)
break;
if (time_after(jiffies, timeout)) {
pr_err("%s: timeout waiting for PLLE\n", __func__);
return -EBUSY;
}
udelay(300);
}
return 0;
}
static int clk_plle_enable(struct clk_hw *hw)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk));
struct tegra_clk_pll_freq_table sel;
u32 val;
int err;
if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
return -EINVAL;
clk_pll_disable(hw);
val = pll_readl_misc(pll);
val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
pll_writel_misc(val, pll);
val = pll_readl_misc(pll);
if (!(val & PLLE_MISC_READY)) {
err = clk_plle_training(pll);
if (err)
return err;
}
if (pll->flags & TEGRA_PLLE_CONFIGURE) {
/* configure dividers */
val = pll_readl_base(pll);
val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll));
val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT);
val |= sel.m << pll->divm_shift;
val |= sel.n << pll->divn_shift;
val |= sel.p << pll->divp_shift;
val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
pll_writel_base(val, pll);
}
val = pll_readl_misc(pll);
val |= PLLE_MISC_SETUP_VALUE;
val |= PLLE_MISC_LOCK_ENABLE;
pll_writel_misc(val, pll);
val = readl(pll->clk_base + PLLE_SS_CTRL);
val |= PLLE_SS_DISABLE;
writel(val, pll->clk_base + PLLE_SS_CTRL);
val |= pll_readl_base(pll);
val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);
pll_writel_base(val, pll);
clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg,
pll->params->lock_bit_idx);
return 0;
}
static unsigned long clk_plle_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra_clk_pll *pll = to_clk_pll(hw);
u32 val = pll_readl_base(pll);
u32 divn = 0, divm = 0, divp = 0;
u64 rate = parent_rate;
divp = (val >> pll->divp_shift) & (divp_mask(pll));
divn = (val >> pll->divn_shift) & (divn_mask(pll));
divm = (val >> pll->divm_shift) & (divm_mask(pll));
divm *= divp;
rate *= divn;
do_div(rate, divm);
return rate;
}
const struct clk_ops tegra_clk_pll_ops = {
.is_enabled = clk_pll_is_enabled,
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
.round_rate = clk_pll_round_rate,
.set_rate = clk_pll_set_rate,
};
const struct clk_ops tegra_clk_plle_ops = {
.recalc_rate = clk_plle_recalc_rate,
.is_enabled = clk_pll_is_enabled,
.disable = clk_pll_disable,
.enable = clk_plle_enable,
};
static 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_freq_table *freq_table, spinlock_t *lock,
const struct clk_ops *ops)
{
struct tegra_clk_pll *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
pll->clk_base = clk_base;
pll->pmc = pmc;
pll->freq_table = freq_table;
pll->params = pll_params;
pll->fixed_rate = fixed_rate;
pll->flags = pll_flags;
pll->lock = lock;
pll->divp_shift = PLL_BASE_DIVP_SHIFT;
pll->divp_width = PLL_BASE_DIVP_WIDTH;
pll->divn_shift = PLL_BASE_DIVN_SHIFT;
pll->divn_width = PLL_BASE_DIVN_WIDTH;
pll->divm_shift = PLL_BASE_DIVM_SHIFT;
pll->divm_width = PLL_BASE_DIVM_WIDTH;
/* Data in .init is copied by clk_register(), so stack variable OK */
pll->hw.init = &init;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk))
kfree(pll);
return clk;
}
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_freq_table *freq_table, spinlock_t *lock)
{
return _tegra_clk_register_pll(name, parent_name, clk_base, pmc,
flags, fixed_rate, pll_params, pll_flags, freq_table,
lock, &tegra_clk_pll_ops);
}
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_freq_table *freq_table, spinlock_t *lock)
{
return _tegra_clk_register_pll(name, parent_name, clk_base, pmc,
flags, fixed_rate, pll_params, pll_flags, freq_table,
lock, &tegra_clk_plle_ops);
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2012, 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/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include "clk.h"
#define SUPER_STATE_IDLE 0
#define SUPER_STATE_RUN 1
#define SUPER_STATE_IRQ 2
#define SUPER_STATE_FIQ 3
#define SUPER_STATE_SHIFT 28
#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \
BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \
<< SUPER_STATE_SHIFT)
#define SUPER_LP_DIV2_BYPASS (1 << 16)
#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT)
#define super_state_to_src_shift(m, s) ((m->width * s))
#define super_state_to_src_mask(m) (((1 << m->width) - 1))
static u8 clk_super_get_parent(struct clk_hw *hw)
{
struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
u32 val, state;
u8 source, shift;
val = readl_relaxed(mux->reg);
state = val & SUPER_STATE_MASK;
BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
(state != super_state(SUPER_STATE_IDLE)));
shift = (state == super_state(SUPER_STATE_IDLE)) ?
super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
super_state_to_src_shift(mux, SUPER_STATE_RUN);
source = (val >> shift) & super_state_to_src_mask(mux);
/*
* If LP_DIV2_BYPASS is not set and PLLX is current parent then
* PLLX/2 is the input source to CCLKLP.
*/
if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) &&
(source == mux->pllx_index))
source = mux->div2_index;
return source;
}
static int clk_super_set_parent(struct clk_hw *hw, u8 index)
{
struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
u32 val, state;
u8 parent_index, shift;
val = readl_relaxed(mux->reg);
state = val & SUPER_STATE_MASK;
BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
(state != super_state(SUPER_STATE_IDLE)));
shift = (state == super_state(SUPER_STATE_IDLE)) ?
super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
super_state_to_src_shift(mux, SUPER_STATE_RUN);
/*
* For LP mode super-clock switch between PLLX direct
* and divided-by-2 outputs is allowed only when other
* than PLLX clock source is current parent.
*/
if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) ||
(index == mux->pllx_index))) {
parent_index = clk_super_get_parent(hw);
if ((parent_index == mux->div2_index) ||
(parent_index == mux->pllx_index))
return -EINVAL;
val ^= SUPER_LP_DIV2_BYPASS;
writel_relaxed(val, mux->reg);
udelay(2);
if (index == mux->div2_index)
index = mux->pllx_index;
}
val &= ~((super_state_to_src_mask(mux)) << shift);
val |= (index & (super_state_to_src_mask(mux))) << shift;
writel_relaxed(val, mux->reg);
udelay(2);
return 0;
}
const struct clk_ops tegra_clk_super_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
};
struct clk *tegra_clk_register_super_mux(const char *name,
const char **parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock)
{
struct tegra_clk_super_mux *super;
struct clk *clk;
struct clk_init_data init;
super = kzalloc(sizeof(*super), GFP_KERNEL);
if (!super) {
pr_err("%s: could not allocate super clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &tegra_clk_super_ops;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
super->reg = reg;
super->pllx_index = pllx_index;
super->div2_index = div2_index;
super->lock = lock;
super->width = width;
super->flags = clk_super_flags;
/* Data in .init is copied by clk_register(), so stack variable OK */
super->hw.init = &init;
clk = clk_register(NULL, &super->hw);
if (IS_ERR(clk))
kfree(super);
return clk;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

85
drivers/clk/tegra/clk.c Normal file
View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2012, 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/of.h>
#include <linux/clk/tegra.h>
#include "clk.h"
/* Global data of Tegra CPU CAR ops */
struct tegra_cpu_car_ops *tegra_cpu_car_ops;
void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
struct clk *clks[], int clk_max)
{
struct clk *clk;
for (; dup_list->clk_id < clk_max; dup_list++) {
clk = clks[dup_list->clk_id];
dup_list->lookup.clk = clk;
clkdev_add(&dup_list->lookup);
}
}
void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
struct clk *clks[], int clk_max)
{
struct clk *clk;
for (; tbl->clk_id < clk_max; tbl++) {
clk = clks[tbl->clk_id];
if (IS_ERR_OR_NULL(clk))
return;
if (tbl->parent_id < clk_max) {
struct clk *parent = clks[tbl->parent_id];
if (clk_set_parent(clk, parent)) {
pr_err("%s: Failed to set parent %s of %s\n",
__func__, __clk_get_name(parent),
__clk_get_name(clk));
WARN_ON(1);
}
}
if (tbl->rate)
if (clk_set_rate(clk, tbl->rate)) {
pr_err("%s: Failed to set rate %lu of %s\n",
__func__, tbl->rate,
__clk_get_name(clk));
WARN_ON(1);
}
if (tbl->state)
if (clk_prepare_enable(clk)) {
pr_err("%s: Failed to enable %s\n", __func__,
__clk_get_name(clk));
WARN_ON(1);
}
}
}
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 },
{ }
};
void __init tegra_clocks_init(void)
{
of_clk_init(tegra_dt_clk_match);
}

502
drivers/clk/tegra/clk.h Normal file
View file

@ -0,0 +1,502 @@
/*
* Copyright (c) 2012, 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/>.
*/
#ifndef __TEGRA_CLK_H
#define __TEGRA_CLK_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
/**
* struct tegra_clk_sync_source - external clock source from codec
*
* @hw: handle between common and hardware-specific interfaces
* @rate: input frequency from source
* @max_rate: max rate allowed
*/
struct tegra_clk_sync_source {
struct clk_hw hw;
unsigned long rate;
unsigned long max_rate;
};
#define to_clk_sync_source(_hw) \
container_of(_hw, struct tegra_clk_sync_source, hw)
extern const struct clk_ops tegra_clk_sync_source_ops;
struct clk *tegra_clk_register_sync_source(const char *name,
unsigned long fixed_rate, unsigned long max_rate);
/**
* struct tegra_clk_frac_div - fractional divider clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register containing divider
* @flags: hardware-specific flags
* @shift: shift to the divider bit field
* @width: width of the divider bit field
* @frac_width: width of the fractional bit field
* @lock: register lock
*
* Flags:
* TEGRA_DIVIDER_ROUND_UP - This flags indicates to round up the divider value.
* TEGRA_DIVIDER_FIXED - Fixed rate PLL dividers has addition override bit, this
* flag indicates that this divider is for fixed rate PLL.
* TEGRA_DIVIDER_INT - Some modules can not cope with the duty cycle when
* fraction bit is set. This flags indicates to calculate divider for which
* fracton bit will be zero.
* TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is
* set when divider value is not 0. This flags indicates that the divider
* is for UART module.
*/
struct tegra_clk_frac_div {
struct clk_hw hw;
void __iomem *reg;
u8 flags;
u8 shift;
u8 width;
u8 frac_width;
spinlock_t *lock;
};
#define to_clk_frac_div(_hw) container_of(_hw, struct tegra_clk_frac_div, hw)
#define TEGRA_DIVIDER_ROUND_UP BIT(0)
#define TEGRA_DIVIDER_FIXED BIT(1)
#define TEGRA_DIVIDER_INT BIT(2)
#define TEGRA_DIVIDER_UART BIT(3)
extern const struct clk_ops tegra_clk_frac_div_ops;
struct clk *tegra_clk_register_divider(const char *name,
const char *parent_name, void __iomem *reg,
unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
u8 frac_width, spinlock_t *lock);
/*
* Tegra PLL:
*
* In general, there are 3 requirements for each PLL
* that SW needs to be comply with.
* (1) Input frequency range (REF).
* (2) Comparison frequency range (CF). CF = REF/DIVM.
* (3) VCO frequency range (VCO). VCO = CF * DIVN.
*
* The final PLL output frequency (FO) = VCO >> DIVP.
*/
/**
* struct tegra_clk_pll_freq_table - PLL frequecy table
*
* @input_rate: input rate from source
* @output_rate: output rate from PLL for the input rate
* @n: feedback divider
* @m: input divider
* @p: post divider
* @cpcon: charge pump current
*/
struct tegra_clk_pll_freq_table {
unsigned long input_rate;
unsigned long output_rate;
u16 n;
u16 m;
u8 p;
u8 cpcon;
};
/**
* struct clk_pll_params - PLL parameters
*
* @input_min: Minimum input frequency
* @input_max: Maximum input frequency
* @cf_min: Minimum comparison frequency
* @cf_max: Maximum comparison frequency
* @vco_min: Minimum VCO frequency
* @vco_max: Maximum VCO frequency
* @base_reg: PLL base reg offset
* @misc_reg: PLL misc reg offset
* @lock_reg: PLL lock reg offset
* @lock_bit_idx: Bit index for PLL lock status
* @lock_enable_bit_idx: Bit index to enable PLL lock
* @lock_delay: Delay in us if PLL lock is not used
*/
struct tegra_clk_pll_params {
unsigned long input_min;
unsigned long input_max;
unsigned long cf_min;
unsigned long cf_max;
unsigned long vco_min;
unsigned long vco_max;
u32 base_reg;
u32 misc_reg;
u32 lock_reg;
u32 lock_bit_idx;
u32 lock_enable_bit_idx;
int lock_delay;
};
/**
* struct tegra_clk_pll - Tegra PLL clock
*
* @hw: handle between common and hardware-specifix interfaces
* @clk_base: address of CAR controller
* @pmc: address of PMC, required to read override bits
* @freq_table: array of frequencies supported by PLL
* @params: PLL parameters
* @flags: PLL flags
* @fixed_rate: PLL rate if it is fixed
* @lock: register lock
* @divn_shift: shift to the feedback divider bit field
* @divn_width: width of the feedback divider bit field
* @divm_shift: shift to the input divider bit field
* @divm_width: width of the input divider bit field
* @divp_shift: shift to the post divider bit field
* @divp_width: width of the post divider bit field
*
* Flags:
* TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for
* PLL locking. If not set it will use lock_delay value to wait.
* TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs
* to be programmed to change output frequency of the PLL.
* TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs
* to be programmed to change output frequency of the PLL.
* TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs
* to be programmed to change output frequency of the PLL.
* TEGRA_PLLU - PLLU has inverted post divider. This flags indicated
* that it is PLLU and invert post divider value.
* TEGRA_PLLM - PLLM has additional override settings in PMC. This
* flag indicates that it is PLLM and use override settings.
* TEGRA_PLL_FIXED - We are not supposed to change output frequency
* of some plls.
* TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling.
*/
struct tegra_clk_pll {
struct clk_hw hw;
void __iomem *clk_base;
void __iomem *pmc;
u8 flags;
unsigned long fixed_rate;
spinlock_t *lock;
u8 divn_shift;
u8 divn_width;
u8 divm_shift;
u8 divm_width;
u8 divp_shift;
u8 divp_width;
struct tegra_clk_pll_freq_table *freq_table;
struct tegra_clk_pll_params *params;
};
#define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw)
#define TEGRA_PLL_USE_LOCK BIT(0)
#define TEGRA_PLL_HAS_CPCON BIT(1)
#define TEGRA_PLL_SET_LFCON BIT(2)
#define TEGRA_PLL_SET_DCCON BIT(3)
#define TEGRA_PLLU BIT(4)
#define TEGRA_PLLM BIT(5)
#define TEGRA_PLL_FIXED BIT(6)
#define TEGRA_PLLE_CONFIGURE BIT(7)
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_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_freq_table *freq_table, spinlock_t *lock);
/**
* struct tegra_clk_pll_out - PLL divider down clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register containing the PLL divider
* @enb_bit_idx: bit to enable/disable PLL divider
* @rst_bit_idx: bit to reset PLL divider
* @lock: register lock
* @flags: hardware-specific flags
*/
struct tegra_clk_pll_out {
struct clk_hw hw;
void __iomem *reg;
u8 enb_bit_idx;
u8 rst_bit_idx;
spinlock_t *lock;
u8 flags;
};
#define to_clk_pll_out(_hw) container_of(_hw, struct tegra_clk_pll_out, hw)
extern const struct clk_ops tegra_clk_pll_out_ops;
struct clk *tegra_clk_register_pll_out(const char *name,
const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
u8 rst_bit_idx, unsigned long flags, u8 pll_div_flags,
spinlock_t *lock);
/**
* struct tegra_clk_periph_regs - Registers controlling peripheral clock
*
* @enb_reg: read the enable status
* @enb_set_reg: write 1 to enable clock
* @enb_clr_reg: write 1 to disable clock
* @rst_reg: read the reset status
* @rst_set_reg: write 1 to assert the reset of peripheral
* @rst_clr_reg: write 1 to deassert the reset of peripheral
*/
struct tegra_clk_periph_regs {
u32 enb_reg;
u32 enb_set_reg;
u32 enb_clr_reg;
u32 rst_reg;
u32 rst_set_reg;
u32 rst_clr_reg;
};
/**
* struct tegra_clk_periph_gate - peripheral gate clock
*
* @magic: magic number to validate type
* @hw: handle between common and hardware-specific interfaces
* @clk_base: address of CAR controller
* @regs: Registers to control the peripheral
* @flags: hardware-specific flags
* @clk_num: Clock number
* @enable_refcnt: array to maintain reference count of the clock
*
* Flags:
* TEGRA_PERIPH_NO_RESET - This flag indicates that reset is not allowed
* for this module.
* TEGRA_PERIPH_MANUAL_RESET - This flag indicates not to reset module
* after clock enable and driver for the module is responsible for
* doing reset.
* 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.
*/
struct tegra_clk_periph_gate {
u32 magic;
struct clk_hw hw;
void __iomem *clk_base;
u8 flags;
int clk_num;
int *enable_refcnt;
struct tegra_clk_periph_regs *regs;
};
#define to_clk_periph_gate(_hw) \
container_of(_hw, struct tegra_clk_periph_gate, hw)
#define TEGRA_CLK_PERIPH_GATE_MAGIC 0x17760309
#define TEGRA_PERIPH_NO_RESET BIT(0)
#define TEGRA_PERIPH_MANUAL_RESET BIT(1)
#define TEGRA_PERIPH_ON_APB BIT(2)
void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert);
extern const struct clk_ops tegra_clk_periph_gate_ops;
struct clk *tegra_clk_register_periph_gate(const char *name,
const char *parent_name, u8 gate_flags, void __iomem *clk_base,
unsigned long flags, int clk_num,
struct tegra_clk_periph_regs *pregs, int *enable_refcnt);
/**
* struct clk-periph - peripheral clock
*
* @magic: magic number to validate type
* @hw: handle between common and hardware-specific interfaces
* @mux: mux clock
* @divider: divider clock
* @gate: gate clock
* @mux_ops: mux clock ops
* @div_ops: divider clock ops
* @gate_ops: gate clock ops
*/
struct tegra_clk_periph {
u32 magic;
struct clk_hw hw;
struct clk_mux mux;
struct tegra_clk_frac_div divider;
struct tegra_clk_periph_gate gate;
const struct clk_ops *mux_ops;
const struct clk_ops *div_ops;
const struct clk_ops *gate_ops;
};
#define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
#define TEGRA_CLK_PERIPH_MAGIC 0x18221223
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);
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, \
_div_shift, _div_width, _div_frac_width, \
_div_flags, _clk_num, _enb_refcnt, _regs, \
_gate_flags) \
{ \
.mux = { \
.flags = _mux_flags, \
.shift = _mux_shift, \
.width = _mux_width, \
}, \
.divider = { \
.flags = _div_flags, \
.shift = _div_shift, \
.width = _div_width, \
.frac_width = _div_frac_width, \
}, \
.gate = { \
.flags = _gate_flags, \
.clk_num = _clk_num, \
.enable_refcnt = _enb_refcnt, \
.regs = _regs, \
}, \
.mux_ops = &clk_mux_ops, \
.div_ops = &tegra_clk_frac_div_ops, \
.gate_ops = &tegra_clk_periph_gate_ops, \
}
struct tegra_periph_init_data {
const char *name;
int clk_id;
const char **parent_names;
int num_parents;
struct tegra_clk_periph periph;
u32 offset;
const char *con_id;
const char *dev_id;
};
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \
_mux_shift, _mux_width, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
{ \
.name = _name, \
.clk_id = _clk_id, \
.parent_names = _parent_names, \
.num_parents = ARRAY_SIZE(_parent_names), \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \
_mux_flags, _div_shift, \
_div_width, _div_frac_width, \
_div_flags, _clk_num, \
_enb_refcnt, _regs, \
_gate_flags), \
.offset = _offset, \
.con_id = _con_id, \
.dev_id = _dev_id, \
}
/**
* struct clk_super_mux - super clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register controlling multiplexer
* @width: width of the multiplexer bit field
* @flags: hardware-specific flags
* @div2_index: bit controlling divide-by-2
* @pllx_index: PLLX index in the parent list
* @lock: register lock
*
* Flags:
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
* that this is LP cluster clock.
*/
struct tegra_clk_super_mux {
struct clk_hw hw;
void __iomem *reg;
u8 width;
u8 flags;
u8 div2_index;
u8 pllx_index;
spinlock_t *lock;
};
#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
#define TEGRA_DIVIDER_2 BIT(0)
extern const struct clk_ops tegra_clk_super_ops;
struct clk *tegra_clk_register_super_mux(const char *name,
const char **parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock);
/**
* struct clk_init_tabel - clock initialization table
* @clk_id: clock id as mentioned in device tree bindings
* @parent_id: parent clock id as mentioned in device tree bindings
* @rate: rate to set
* @state: enable/disable
*/
struct tegra_clk_init_table {
unsigned int clk_id;
unsigned int parent_id;
unsigned long rate;
int state;
};
/**
* struct clk_duplicate - duplicate clocks
* @clk_id: clock id as mentioned in device tree bindings
* @lookup: duplicate lookup entry for the clock
*/
struct tegra_clk_duplicate {
int clk_id;
struct clk_lookup lookup;
};
#define TEGRA_CLK_DUPLICATE(_clk_id, _dev, _con) \
{ \
.clk_id = _clk_id, \
.lookup = { \
.dev_id = _dev, \
.con_id = _con, \
}, \
}
void tegra_init_from_table(struct tegra_clk_init_table *tbl,
struct clk *clks[], int clk_max);
void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
struct clk *clks[], int clk_max);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
void tegra20_clock_init(struct device_node *np);
#else
static inline void tegra20_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA_2x_SOC */
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
void tegra30_clock_init(struct device_node *np);
#else
static inline void tegra30_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA_3x_SOC */
#endif /* TEGRA_CLK_H */

View file

@ -17,6 +17,7 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o

View file

@ -1,6 +1,4 @@
/*
* arch/arch/mach-tegra/timer.c
*
* Copyright (C) 2010 Google, Inc.
*
* Author:
@ -33,8 +31,6 @@
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
#include "board.h"
#define RTC_SECONDS 0x08
#define RTC_SHADOW_SECONDS 0x0c
#define RTC_MILLISECONDS 0x10
@ -168,7 +164,7 @@ static const struct of_device_id rtc_match[] __initconst = {
{}
};
void __init tegra_init_timer(void)
static void __init tegra20_init_timer(void)
{
struct device_node *np;
struct clk *clk;
@ -183,7 +179,7 @@ void __init tegra_init_timer(void)
timer_reg_base = of_iomap(np, 0);
if (!timer_reg_base) {
pr_err("Can't map timer registers");
pr_err("Can't map timer registers\n");
BUG();
}
@ -268,6 +264,7 @@ void __init tegra_init_timer(void)
#endif
register_persistent_clock(NULL, tegra_read_persistent_clock);
}
CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer);
#ifdef CONFIG_PM
static u32 usec_config;

View file

@ -31,8 +31,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/clk/tegra.h>
#include <mach/clk.h>
#include "dmaengine.h"
#define TEGRA_APBDMA_GENERAL 0x0

View file

@ -12,8 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#include "drm.h"
#include "dc.h"

View file

@ -11,7 +11,6 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <mach/clk.h>
#include <linux/dma-mapping.h>
#include <asm/dma-iommu.h>

View file

@ -14,8 +14,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#include "hdmi.h"
#include "drm.h"

View file

@ -29,11 +29,10 @@
#include <linux/of_i2c.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/clk/tegra.h>
#include <asm/unaligned.h>
#include <mach/clk.h>
#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
#define BYTES_PER_FIFO_WORD 4

View file

@ -30,7 +30,7 @@
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/input/tegra_kbc.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu

View file

@ -34,7 +34,7 @@
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-tegra.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#define SPI_COMMAND 0x000
#define SPI_GO BIT(30)
@ -525,7 +525,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
goto exit_free_master;
}
tsd->clk = devm_clk_get(&pdev->dev, "spi");
tsd->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tsd->clk)) {
dev_err(&pdev->dev, "can not get clock\n");
ret = PTR_ERR(tsd->clk);

View file

@ -35,7 +35,7 @@
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-tegra.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#define SLINK_COMMAND 0x000
#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
@ -1191,7 +1191,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
goto exit_free_master;
}
tspi->clk = devm_clk_get(&pdev->dev, "slink");
tspi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tspi->clk)) {
dev_err(&pdev->dev, "can not get clock\n");
ret = PTR_ERR(tspi->clk);

View file

@ -1,9 +1,5 @@
ToDo list (incomplete, unordered)
- add compile as module support
- fix clk usage
should not be using clk_get_sys(), but clk_get(&pdev->dev, conn)
where conn is either NULL if the device only has one clock, or
the device specific name if it has multiple clocks.
- move half of the nvec init stuff to i2c-tegra.c
- move event handling to nvec_events
- finish suspend/resume support

View file

@ -37,8 +37,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#include "nvec.h"
@ -771,7 +770,7 @@ static int tegra_nvec_probe(struct platform_device *pdev)
return -ENODEV;
}
i2c_clk = clk_get_sys("tegra-i2c.2", "div-clk");
i2c_clk = clk_get(&pdev->dev, "div-clk");
if (IS_ERR(i2c_clk)) {
dev_err(nvec->dev, "failed to get controller clock\n");
return -ENODEV;

View file

@ -14,8 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MACH_TEGRA_CPU_CAR_H
#define __MACH_TEGRA_CPU_CAR_H
#ifndef __LINUX_CLK_TEGRA_H_
#define __LINUX_CLK_TEGRA_H_
#include <linux/clk.h>
/*
* Tegra CPU clock and reset control ops
@ -118,7 +120,8 @@ static inline void tegra_cpu_clock_resume(void)
}
#endif
void tegra20_cpu_car_ops_init(void);
void tegra30_cpu_car_ops_init(void);
void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
void tegra_clocks_init(void);
#endif /* __MACH_TEGRA_CPU_CAR_H */
#endif /* __LINUX_CLK_TEGRA_H_ */

22
include/linux/tegra-soc.h Normal file
View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2012, 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/>.
*/
#ifndef __LINUX_TEGRA_SOC_H_
#define __LINUX_TEGRA_SOC_H_
u32 tegra_read_chipid(void);
#endif /* __LINUX_TEGRA_SOC_H_ */

View file

@ -25,7 +25,7 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <mach/clk.h>
#include <linux/clk/tegra.h>
#include <sound/soc.h>
#include "tegra30_ahub.h"
@ -299,15 +299,6 @@ static const char * const configlink_clocks[] = {
"spdif_in",
};
struct of_dev_auxdata ahub_auxdata[] = {
OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL),
{}
};
#define LAST_REG(name) \
(TEGRA30_AHUB_##name + \
(TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4)
@ -451,7 +442,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
* Ensure that here.
*/
for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
clk = clk_get_sys(NULL, configlink_clocks[i]);
clk = clk_get(&pdev->dev, configlink_clocks[i]);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Can't get clock %s\n",
configlink_clocks[i]);
@ -569,8 +560,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
goto err_pm_disable;
}
of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata,
&pdev->dev);
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
return 0;