diff options
author | Paul Burton <paul.burton@mips.com> | 2019-08-08 18:33:16 -0400 |
---|---|---|
committer | Paul Burton <paul.burton@mips.com> | 2019-08-08 18:33:16 -0400 |
commit | 75b7329a4f08b3d2566afa25cfaddc8ddfb6dfa1 (patch) | |
tree | 17a4e177a2a245af450827330eef0f39f16be3cf | |
parent | 6393e60644862478a9da8c6599ffc04f7f94bedf (diff) | |
parent | abc552284f6b1e8e6f153771dac1dff72e9d6d66 (diff) |
Merge branch 'ingenic-tcu-v5.4' into mips-next
Merge the Ingenic TCU patchset from the ingenic-tcu-v5.4 branch which
was created to enable follow-on changes in other subsystems.
Signed-off-by: Paul Burton <paul.burton@mips.com>
27 files changed, 1421 insertions, 206 deletions
diff --git a/Documentation/devicetree/bindings/pwm/ingenic,jz47xx-pwm.txt b/Documentation/devicetree/bindings/pwm/ingenic,jz47xx-pwm.txt deleted file mode 100644 index 493bec80d59b..000000000000 --- a/Documentation/devicetree/bindings/pwm/ingenic,jz47xx-pwm.txt +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | Ingenic JZ47xx PWM Controller | ||
2 | ============================= | ||
3 | |||
4 | Required properties: | ||
5 | - compatible: Should be "ingenic,jz4740-pwm" | ||
6 | - #pwm-cells: Should be 3. See pwm.txt in this directory for a description | ||
7 | of the cells format. | ||
8 | - clocks : phandle to the external clock. | ||
9 | - clock-names : Should be "ext". | ||
10 | |||
11 | |||
12 | Example: | ||
13 | |||
14 | pwm: pwm@10002000 { | ||
15 | compatible = "ingenic,jz4740-pwm"; | ||
16 | reg = <0x10002000 0x1000>; | ||
17 | |||
18 | #pwm-cells = <3>; | ||
19 | |||
20 | clocks = <&ext>; | ||
21 | clock-names = "ext"; | ||
22 | }; | ||
diff --git a/Documentation/devicetree/bindings/timer/ingenic,tcu.txt b/Documentation/devicetree/bindings/timer/ingenic,tcu.txt new file mode 100644 index 000000000000..5a4b9ddd9470 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/ingenic,tcu.txt | |||
@@ -0,0 +1,137 @@ | |||
1 | Ingenic JZ47xx SoCs Timer/Counter Unit devicetree bindings | ||
2 | ========================================================== | ||
3 | |||
4 | For a description of the TCU hardware and drivers, have a look at | ||
5 | Documentation/mips/ingenic-tcu.txt. | ||
6 | |||
7 | Required properties: | ||
8 | |||
9 | - compatible: Must be one of: | ||
10 | * ingenic,jz4740-tcu | ||
11 | * ingenic,jz4725b-tcu | ||
12 | * ingenic,jz4770-tcu | ||
13 | followed by "simple-mfd". | ||
14 | - reg: Should be the offset/length value corresponding to the TCU registers | ||
15 | - clocks: List of phandle & clock specifiers for clocks external to the TCU. | ||
16 | The "pclk", "rtc" and "ext" clocks should be provided. The "tcu" clock | ||
17 | should be provided if the SoC has it. | ||
18 | - clock-names: List of name strings for the external clocks. | ||
19 | - #clock-cells: Should be <1>; | ||
20 | Clock consumers specify this argument to identify a clock. The valid values | ||
21 | may be found in <dt-bindings/clock/ingenic,tcu.h>. | ||
22 | - interrupt-controller : Identifies the node as an interrupt controller | ||
23 | - #interrupt-cells : Specifies the number of cells needed to encode an | ||
24 | interrupt source. The value should be 1. | ||
25 | - interrupts : Specifies the interrupt the controller is connected to. | ||
26 | |||
27 | Optional properties: | ||
28 | |||
29 | - ingenic,pwm-channels-mask: Bitmask of TCU channels reserved for PWM use. | ||
30 | Default value is 0xfc. | ||
31 | |||
32 | |||
33 | Children nodes | ||
34 | ========================================================== | ||
35 | |||
36 | |||
37 | PWM node: | ||
38 | --------- | ||
39 | |||
40 | Required properties: | ||
41 | |||
42 | - compatible: Must be one of: | ||
43 | * ingenic,jz4740-pwm | ||
44 | * ingenic,jz4725b-pwm | ||
45 | - #pwm-cells: Should be 3. See ../pwm/pwm.txt for a description of the cell | ||
46 | format. | ||
47 | - clocks: List of phandle & clock specifiers for the TCU clocks. | ||
48 | - clock-names: List of name strings for the TCU clocks. | ||
49 | |||
50 | |||
51 | Watchdog node: | ||
52 | -------------- | ||
53 | |||
54 | Required properties: | ||
55 | |||
56 | - compatible: Must be "ingenic,jz4740-watchdog" | ||
57 | - clocks: phandle to the WDT clock | ||
58 | - clock-names: should be "wdt" | ||
59 | |||
60 | |||
61 | OS Timer node: | ||
62 | --------- | ||
63 | |||
64 | Required properties: | ||
65 | |||
66 | - compatible: Must be one of: | ||
67 | * ingenic,jz4725b-ost | ||
68 | * ingenic,jz4770-ost | ||
69 | - clocks: phandle to the OST clock | ||
70 | - clock-names: should be "ost" | ||
71 | - interrupts : Specifies the interrupt the OST is connected to. | ||
72 | |||
73 | |||
74 | Example | ||
75 | ========================================================== | ||
76 | |||
77 | #include <dt-bindings/clock/jz4770-cgu.h> | ||
78 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
79 | |||
80 | / { | ||
81 | tcu: timer@10002000 { | ||
82 | compatible = "ingenic,jz4770-tcu", "simple-mfd"; | ||
83 | reg = <0x10002000 0x1000>; | ||
84 | #address-cells = <1>; | ||
85 | #size-cells = <1>; | ||
86 | ranges = <0x0 0x10002000 0x1000>; | ||
87 | |||
88 | #clock-cells = <1>; | ||
89 | |||
90 | clocks = <&cgu JZ4770_CLK_RTC | ||
91 | &cgu JZ4770_CLK_EXT | ||
92 | &cgu JZ4770_CLK_PCLK>; | ||
93 | clock-names = "rtc", "ext", "pclk"; | ||
94 | |||
95 | interrupt-controller; | ||
96 | #interrupt-cells = <1>; | ||
97 | |||
98 | interrupt-parent = <&intc>; | ||
99 | interrupts = <27 26 25>; | ||
100 | |||
101 | watchdog: watchdog@0 { | ||
102 | compatible = "ingenic,jz4740-watchdog"; | ||
103 | reg = <0x0 0xc>; | ||
104 | |||
105 | clocks = <&tcu TCU_CLK_WDT>; | ||
106 | clock-names = "wdt"; | ||
107 | }; | ||
108 | |||
109 | pwm: pwm@40 { | ||
110 | compatible = "ingenic,jz4740-pwm"; | ||
111 | reg = <0x40 0x80>; | ||
112 | |||
113 | #pwm-cells = <3>; | ||
114 | |||
115 | clocks = <&tcu TCU_CLK_TIMER0 | ||
116 | &tcu TCU_CLK_TIMER1 | ||
117 | &tcu TCU_CLK_TIMER2 | ||
118 | &tcu TCU_CLK_TIMER3 | ||
119 | &tcu TCU_CLK_TIMER4 | ||
120 | &tcu TCU_CLK_TIMER5 | ||
121 | &tcu TCU_CLK_TIMER6 | ||
122 | &tcu TCU_CLK_TIMER7>; | ||
123 | clock-names = "timer0", "timer1", "timer2", "timer3", | ||
124 | "timer4", "timer5", "timer6", "timer7"; | ||
125 | }; | ||
126 | |||
127 | ost: timer@e0 { | ||
128 | compatible = "ingenic,jz4770-ost"; | ||
129 | reg = <0xe0 0x20>; | ||
130 | |||
131 | clocks = <&tcu TCU_CLK_OST>; | ||
132 | clock-names = "ost"; | ||
133 | |||
134 | interrupts = <15>; | ||
135 | }; | ||
136 | }; | ||
137 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt b/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt deleted file mode 100644 index ce1cb72d5345..000000000000 --- a/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | Ingenic Watchdog Timer (WDT) Controller for JZ4740 & JZ4780 | ||
2 | |||
3 | Required properties: | ||
4 | compatible: "ingenic,jz4740-watchdog" or "ingenic,jz4780-watchdog" | ||
5 | reg: Register address and length for watchdog registers | ||
6 | clocks: phandle to the RTC clock | ||
7 | clock-names: should be "rtc" | ||
8 | |||
9 | Example: | ||
10 | |||
11 | watchdog: jz4740-watchdog@10002000 { | ||
12 | compatible = "ingenic,jz4740-watchdog"; | ||
13 | reg = <0x10002000 0x10>; | ||
14 | |||
15 | clocks = <&cgu JZ4740_CLK_RTC>; | ||
16 | clock-names = "rtc"; | ||
17 | }; | ||
diff --git a/Documentation/index.rst b/Documentation/index.rst index 70ae148ec980..87214feda41f 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst | |||
@@ -143,6 +143,7 @@ implementation. | |||
143 | arm64/index | 143 | arm64/index |
144 | ia64/index | 144 | ia64/index |
145 | m68k/index | 145 | m68k/index |
146 | mips/index | ||
146 | riscv/index | 147 | riscv/index |
147 | s390/index | 148 | s390/index |
148 | sh/index | 149 | sh/index |
diff --git a/Documentation/mips/index.rst b/Documentation/mips/index.rst new file mode 100644 index 000000000000..321b4794f3b8 --- /dev/null +++ b/Documentation/mips/index.rst | |||
@@ -0,0 +1,11 @@ | |||
1 | .. SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | =========================== | ||
4 | MIPS-specific Documentation | ||
5 | =========================== | ||
6 | |||
7 | .. toctree:: | ||
8 | :maxdepth: 1 | ||
9 | :numbered: | ||
10 | |||
11 | ingenic-tcu | ||
diff --git a/Documentation/mips/ingenic-tcu.rst b/Documentation/mips/ingenic-tcu.rst new file mode 100644 index 000000000000..c4ef4c45aade --- /dev/null +++ b/Documentation/mips/ingenic-tcu.rst | |||
@@ -0,0 +1,71 @@ | |||
1 | .. SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | =============================================== | ||
4 | Ingenic JZ47xx SoCs Timer/Counter Unit hardware | ||
5 | =============================================== | ||
6 | |||
7 | The Timer/Counter Unit (TCU) in Ingenic JZ47xx SoCs is a multi-function | ||
8 | hardware block. It features up to to eight channels, that can be used as | ||
9 | counters, timers, or PWM. | ||
10 | |||
11 | - JZ4725B, JZ4750, JZ4755 only have six TCU channels. The other SoCs all | ||
12 | have eight channels. | ||
13 | |||
14 | - JZ4725B introduced a separate channel, called Operating System Timer | ||
15 | (OST). It is a 32-bit programmable timer. On JZ4760B and above, it is | ||
16 | 64-bit. | ||
17 | |||
18 | - Each one of the TCU channels has its own clock, which can be reparented to three | ||
19 | different clocks (pclk, ext, rtc), gated, and reclocked, through their TCSR register. | ||
20 | |||
21 | - The watchdog and OST hardware blocks also feature a TCSR register with the same | ||
22 | format in their register space. | ||
23 | - The TCU registers used to gate/ungate can also gate/ungate the watchdog and | ||
24 | OST clocks. | ||
25 | |||
26 | - Each TCU channel works in one of two modes: | ||
27 | |||
28 | - mode TCU1: channels cannot work in sleep mode, but are easier to | ||
29 | operate. | ||
30 | - mode TCU2: channels can work in sleep mode, but the operation is a bit | ||
31 | more complicated than with TCU1 channels. | ||
32 | |||
33 | - The mode of each TCU channel depends on the SoC used: | ||
34 | |||
35 | - On the oldest SoCs (up to JZ4740), all of the eight channels operate in | ||
36 | TCU1 mode. | ||
37 | - On JZ4725B, channel 5 operates as TCU2, the others operate as TCU1. | ||
38 | - On newest SoCs (JZ4750 and above), channels 1-2 operate as TCU2, the | ||
39 | others operate as TCU1. | ||
40 | |||
41 | - Each channel can generate an interrupt. Some channels share an interrupt | ||
42 | line, some don't, and this changes between SoC versions: | ||
43 | |||
44 | - on older SoCs (JZ4740 and below), channel 0 and channel 1 have their | ||
45 | own interrupt line; channels 2-7 share the last interrupt line. | ||
46 | - On JZ4725B, channel 0 has its own interrupt; channels 1-5 share one | ||
47 | interrupt line; the OST uses the last interrupt line. | ||
48 | - on newer SoCs (JZ4750 and above), channel 5 has its own interrupt; | ||
49 | channels 0-4 and (if eight channels) 6-7 all share one interrupt line; | ||
50 | the OST uses the last interrupt line. | ||
51 | |||
52 | Implementation | ||
53 | ============== | ||
54 | |||
55 | The functionalities of the TCU hardware are spread across multiple drivers: | ||
56 | |||
57 | =========== ===== | ||
58 | clocks drivers/clk/ingenic/tcu.c | ||
59 | interrupts drivers/irqchip/irq-ingenic-tcu.c | ||
60 | timers drivers/clocksource/ingenic-timer.c | ||
61 | OST drivers/clocksource/ingenic-ost.c | ||
62 | PWM drivers/pwm/pwm-jz4740.c | ||
63 | watchdog drivers/watchdog/jz4740_wdt.c | ||
64 | =========== ===== | ||
65 | |||
66 | Because various functionalities of the TCU that belong to different drivers | ||
67 | and frameworks can be controlled from the same registers, all of these | ||
68 | drivers access their registers through the same regmap. | ||
69 | |||
70 | For more information regarding the devicetree bindings of the TCU drivers, | ||
71 | have a look at Documentation/devicetree/bindings/mfd/ingenic,tcu.txt. | ||
diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index 4f7b1fa31cf5..2e9952311ecd 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts | |||
@@ -2,6 +2,7 @@ | |||
2 | /dts-v1/; | 2 | /dts-v1/; |
3 | 3 | ||
4 | #include "jz4780.dtsi" | 4 | #include "jz4780.dtsi" |
5 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
5 | #include <dt-bindings/gpio/gpio.h> | 6 | #include <dt-bindings/gpio/gpio.h> |
6 | 7 | ||
7 | / { | 8 | / { |
@@ -238,3 +239,9 @@ | |||
238 | bias-disable; | 239 | bias-disable; |
239 | }; | 240 | }; |
240 | }; | 241 | }; |
242 | |||
243 | &tcu { | ||
244 | /* 3 MHz for the system timer and clocksource */ | ||
245 | assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>; | ||
246 | assigned-clock-rates = <3000000>, <3000000>; | ||
247 | }; | ||
diff --git a/arch/mips/boot/dts/ingenic/gcw0.dts b/arch/mips/boot/dts/ingenic/gcw0.dts index 35f0291e8d38..f58d239c2058 100644 --- a/arch/mips/boot/dts/ingenic/gcw0.dts +++ b/arch/mips/boot/dts/ingenic/gcw0.dts | |||
@@ -2,6 +2,7 @@ | |||
2 | /dts-v1/; | 2 | /dts-v1/; |
3 | 3 | ||
4 | #include "jz4770.dtsi" | 4 | #include "jz4770.dtsi" |
5 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
5 | 6 | ||
6 | / { | 7 | / { |
7 | compatible = "gcw,zero", "ingenic,jz4770"; | 8 | compatible = "gcw,zero", "ingenic,jz4770"; |
@@ -60,3 +61,12 @@ | |||
60 | /* The WiFi module is connected to the UHC. */ | 61 | /* The WiFi module is connected to the UHC. */ |
61 | status = "okay"; | 62 | status = "okay"; |
62 | }; | 63 | }; |
64 | |||
65 | &tcu { | ||
66 | /* 750 kHz for the system timer and clocksource */ | ||
67 | assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>; | ||
68 | assigned-clock-rates = <750000>, <750000>; | ||
69 | |||
70 | /* PWM1 is in use, so reserve channel #2 for the clocksource */ | ||
71 | ingenic,pwm-channels-mask = <0xfa>; | ||
72 | }; | ||
diff --git a/arch/mips/boot/dts/ingenic/jz4740.dtsi b/arch/mips/boot/dts/ingenic/jz4740.dtsi index bceabf494af5..5accda2767be 100644 --- a/arch/mips/boot/dts/ingenic/jz4740.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi | |||
@@ -53,6 +53,28 @@ | |||
53 | clock-names = "rtc"; | 53 | clock-names = "rtc"; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | tcu: timer@10002000 { | ||
57 | compatible = "ingenic,jz4740-tcu", "simple-mfd"; | ||
58 | reg = <0x10002000 0x1000>; | ||
59 | #address-cells = <1>; | ||
60 | #size-cells = <1>; | ||
61 | ranges = <0x0 0x10002000 0x1000>; | ||
62 | |||
63 | #clock-cells = <1>; | ||
64 | |||
65 | clocks = <&cgu JZ4740_CLK_RTC | ||
66 | &cgu JZ4740_CLK_EXT | ||
67 | &cgu JZ4740_CLK_PCLK | ||
68 | &cgu JZ4740_CLK_TCU>; | ||
69 | clock-names = "rtc", "ext", "pclk", "tcu"; | ||
70 | |||
71 | interrupt-controller; | ||
72 | #interrupt-cells = <1>; | ||
73 | |||
74 | interrupt-parent = <&intc>; | ||
75 | interrupts = <23 22 21>; | ||
76 | }; | ||
77 | |||
56 | rtc_dev: rtc@10003000 { | 78 | rtc_dev: rtc@10003000 { |
57 | compatible = "ingenic,jz4740-rtc"; | 79 | compatible = "ingenic,jz4740-rtc"; |
58 | reg = <0x10003000 0x40>; | 80 | reg = <0x10003000 0x40>; |
diff --git a/arch/mips/boot/dts/ingenic/jz4770.dtsi b/arch/mips/boot/dts/ingenic/jz4770.dtsi index 49ede6c14ff3..0bfb9edff3d0 100644 --- a/arch/mips/boot/dts/ingenic/jz4770.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4770.dtsi | |||
@@ -46,6 +46,27 @@ | |||
46 | #clock-cells = <1>; | 46 | #clock-cells = <1>; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | tcu: timer@10002000 { | ||
50 | compatible = "ingenic,jz4770-tcu", "simple-mfd"; | ||
51 | reg = <0x10002000 0x1000>; | ||
52 | #address-cells = <1>; | ||
53 | #size-cells = <1>; | ||
54 | ranges = <0x0 0x10002000 0x1000>; | ||
55 | |||
56 | #clock-cells = <1>; | ||
57 | |||
58 | clocks = <&cgu JZ4770_CLK_RTC | ||
59 | &cgu JZ4770_CLK_EXT | ||
60 | &cgu JZ4770_CLK_PCLK>; | ||
61 | clock-names = "rtc", "ext", "pclk"; | ||
62 | |||
63 | interrupt-controller; | ||
64 | #interrupt-cells = <1>; | ||
65 | |||
66 | interrupt-parent = <&intc>; | ||
67 | interrupts = <27 26 25>; | ||
68 | }; | ||
69 | |||
49 | pinctrl: pin-controller@10010000 { | 70 | pinctrl: pin-controller@10010000 { |
50 | compatible = "ingenic,jz4770-pinctrl"; | 71 | compatible = "ingenic,jz4770-pinctrl"; |
51 | reg = <0x10010000 0x600>; | 72 | reg = <0x10010000 0x600>; |
diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index b03cdec56de9..c54bd7cfec55 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi | |||
@@ -46,6 +46,29 @@ | |||
46 | #clock-cells = <1>; | 46 | #clock-cells = <1>; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | tcu: timer@10002000 { | ||
50 | compatible = "ingenic,jz4780-tcu", | ||
51 | "ingenic,jz4770-tcu", | ||
52 | "simple-mfd"; | ||
53 | reg = <0x10002000 0x1000>; | ||
54 | #address-cells = <1>; | ||
55 | #size-cells = <1>; | ||
56 | ranges = <0x0 0x10002000 0x1000>; | ||
57 | |||
58 | #clock-cells = <1>; | ||
59 | |||
60 | clocks = <&cgu JZ4780_CLK_RTCLK | ||
61 | &cgu JZ4780_CLK_EXCLK | ||
62 | &cgu JZ4780_CLK_PCLK>; | ||
63 | clock-names = "rtc", "ext", "pclk"; | ||
64 | |||
65 | interrupt-controller; | ||
66 | #interrupt-cells = <1>; | ||
67 | |||
68 | interrupt-parent = <&intc>; | ||
69 | interrupts = <27 26 25>; | ||
70 | }; | ||
71 | |||
49 | rtc_dev: rtc@10003000 { | 72 | rtc_dev: rtc@10003000 { |
50 | compatible = "ingenic,jz4780-rtc"; | 73 | compatible = "ingenic,jz4780-rtc"; |
51 | reg = <0x10003000 0x4c>; | 74 | reg = <0x10003000 0x4c>; |
diff --git a/arch/mips/boot/dts/ingenic/qi_lb60.dts b/arch/mips/boot/dts/ingenic/qi_lb60.dts index 51a0443559e3..7a371d9c5a33 100644 --- a/arch/mips/boot/dts/ingenic/qi_lb60.dts +++ b/arch/mips/boot/dts/ingenic/qi_lb60.dts | |||
@@ -2,10 +2,10 @@ | |||
2 | /dts-v1/; | 2 | /dts-v1/; |
3 | 3 | ||
4 | #include "jz4740.dtsi" | 4 | #include "jz4740.dtsi" |
5 | #include <dt-bindings/gpio/gpio.h> | ||
6 | 5 | ||
7 | #include <dt-bindings/gpio/gpio.h> | 6 | #include <dt-bindings/gpio/gpio.h> |
8 | #include <dt-bindings/iio/adc/ingenic,adc.h> | 7 | #include <dt-bindings/iio/adc/ingenic,adc.h> |
8 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
9 | #include <dt-bindings/input/input.h> | 9 | #include <dt-bindings/input/input.h> |
10 | 10 | ||
11 | #define KEY_QI_QI KEY_F13 | 11 | #define KEY_QI_QI KEY_F13 |
@@ -350,3 +350,9 @@ | |||
350 | pinctrl-names = "default"; | 350 | pinctrl-names = "default"; |
351 | pinctrl-0 = <&pins_mmc>; | 351 | pinctrl-0 = <&pins_mmc>; |
352 | }; | 352 | }; |
353 | |||
354 | &tcu { | ||
355 | /* 750 kHz for the system timer and clocksource */ | ||
356 | assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>; | ||
357 | assigned-clock-rates = <750000>, <750000>; | ||
358 | }; | ||
diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c index cb768e560d8b..5476899f0882 100644 --- a/arch/mips/jz4740/time.c +++ b/arch/mips/jz4740/time.c | |||
@@ -4,161 +4,14 @@ | |||
4 | * JZ4740 platform time support | 4 | * JZ4740 platform time support |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/clk.h> | ||
8 | #include <linux/clk-provider.h> | 7 | #include <linux/clk-provider.h> |
9 | #include <linux/interrupt.h> | 8 | #include <linux/clocksource.h> |
10 | #include <linux/kernel.h> | ||
11 | #include <linux/time.h> | ||
12 | 9 | ||
13 | #include <linux/clockchips.h> | ||
14 | #include <linux/sched_clock.h> | ||
15 | |||
16 | #include <asm/mach-jz4740/irq.h> | ||
17 | #include <asm/mach-jz4740/timer.h> | 10 | #include <asm/mach-jz4740/timer.h> |
18 | #include <asm/time.h> | ||
19 | |||
20 | #define TIMER_CLOCKEVENT 0 | ||
21 | #define TIMER_CLOCKSOURCE 1 | ||
22 | |||
23 | static uint16_t jz4740_jiffies_per_tick; | ||
24 | |||
25 | static u64 jz4740_clocksource_read(struct clocksource *cs) | ||
26 | { | ||
27 | return jz4740_timer_get_count(TIMER_CLOCKSOURCE); | ||
28 | } | ||
29 | |||
30 | static struct clocksource jz4740_clocksource = { | ||
31 | .name = "jz4740-timer", | ||
32 | .rating = 200, | ||
33 | .read = jz4740_clocksource_read, | ||
34 | .mask = CLOCKSOURCE_MASK(16), | ||
35 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
36 | }; | ||
37 | |||
38 | static u64 notrace jz4740_read_sched_clock(void) | ||
39 | { | ||
40 | return jz4740_timer_get_count(TIMER_CLOCKSOURCE); | ||
41 | } | ||
42 | |||
43 | static irqreturn_t jz4740_clockevent_irq(int irq, void *devid) | ||
44 | { | ||
45 | struct clock_event_device *cd = devid; | ||
46 | |||
47 | jz4740_timer_ack_full(TIMER_CLOCKEVENT); | ||
48 | |||
49 | if (!clockevent_state_periodic(cd)) | ||
50 | jz4740_timer_disable(TIMER_CLOCKEVENT); | ||
51 | |||
52 | cd->event_handler(cd); | ||
53 | |||
54 | return IRQ_HANDLED; | ||
55 | } | ||
56 | |||
57 | static int jz4740_clockevent_set_periodic(struct clock_event_device *evt) | ||
58 | { | ||
59 | jz4740_timer_set_count(TIMER_CLOCKEVENT, 0); | ||
60 | jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick); | ||
61 | jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT); | ||
62 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static int jz4740_clockevent_resume(struct clock_event_device *evt) | ||
68 | { | ||
69 | jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT); | ||
70 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int jz4740_clockevent_shutdown(struct clock_event_device *evt) | ||
76 | { | ||
77 | jz4740_timer_disable(TIMER_CLOCKEVENT); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int jz4740_clockevent_set_next(unsigned long evt, | ||
83 | struct clock_event_device *cd) | ||
84 | { | ||
85 | jz4740_timer_set_count(TIMER_CLOCKEVENT, 0); | ||
86 | jz4740_timer_set_period(TIMER_CLOCKEVENT, evt); | ||
87 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static struct clock_event_device jz4740_clockevent = { | ||
93 | .name = "jz4740-timer", | ||
94 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
95 | .set_next_event = jz4740_clockevent_set_next, | ||
96 | .set_state_shutdown = jz4740_clockevent_shutdown, | ||
97 | .set_state_periodic = jz4740_clockevent_set_periodic, | ||
98 | .set_state_oneshot = jz4740_clockevent_shutdown, | ||
99 | .tick_resume = jz4740_clockevent_resume, | ||
100 | .rating = 200, | ||
101 | #ifdef CONFIG_MACH_JZ4740 | ||
102 | .irq = JZ4740_IRQ_TCU0, | ||
103 | #endif | ||
104 | #if defined(CONFIG_MACH_JZ4770) || defined(CONFIG_MACH_JZ4780) | ||
105 | .irq = JZ4780_IRQ_TCU2, | ||
106 | #endif | ||
107 | }; | ||
108 | |||
109 | static struct irqaction timer_irqaction = { | ||
110 | .handler = jz4740_clockevent_irq, | ||
111 | .flags = IRQF_PERCPU | IRQF_TIMER, | ||
112 | .name = "jz4740-timerirq", | ||
113 | .dev_id = &jz4740_clockevent, | ||
114 | }; | ||
115 | 11 | ||
116 | void __init plat_time_init(void) | 12 | void __init plat_time_init(void) |
117 | { | 13 | { |
118 | int ret; | ||
119 | uint32_t clk_rate; | ||
120 | uint16_t ctrl; | ||
121 | struct clk *ext_clk; | ||
122 | |||
123 | of_clk_init(NULL); | 14 | of_clk_init(NULL); |
124 | jz4740_timer_init(); | 15 | jz4740_timer_init(); |
125 | 16 | timer_probe(); | |
126 | ext_clk = clk_get(NULL, "ext"); | ||
127 | if (IS_ERR(ext_clk)) | ||
128 | panic("unable to get ext clock"); | ||
129 | clk_rate = clk_get_rate(ext_clk) >> 4; | ||
130 | clk_put(ext_clk); | ||
131 | |||
132 | jz4740_jiffies_per_tick = DIV_ROUND_CLOSEST(clk_rate, HZ); | ||
133 | |||
134 | clockevent_set_clock(&jz4740_clockevent, clk_rate); | ||
135 | jz4740_clockevent.min_delta_ns = clockevent_delta2ns(100, &jz4740_clockevent); | ||
136 | jz4740_clockevent.min_delta_ticks = 100; | ||
137 | jz4740_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, &jz4740_clockevent); | ||
138 | jz4740_clockevent.max_delta_ticks = 0xffff; | ||
139 | jz4740_clockevent.cpumask = cpumask_of(0); | ||
140 | |||
141 | clockevents_register_device(&jz4740_clockevent); | ||
142 | |||
143 | ret = clocksource_register_hz(&jz4740_clocksource, clk_rate); | ||
144 | |||
145 | if (ret) | ||
146 | printk(KERN_ERR "Failed to register clocksource: %d\n", ret); | ||
147 | |||
148 | sched_clock_register(jz4740_read_sched_clock, 16, clk_rate); | ||
149 | |||
150 | setup_irq(jz4740_clockevent.irq, &timer_irqaction); | ||
151 | |||
152 | ctrl = JZ_TIMER_CTRL_PRESCALE_16 | JZ_TIMER_CTRL_SRC_EXT; | ||
153 | |||
154 | jz4740_timer_set_ctrl(TIMER_CLOCKEVENT, ctrl); | ||
155 | jz4740_timer_set_ctrl(TIMER_CLOCKSOURCE, ctrl); | ||
156 | |||
157 | jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick); | ||
158 | jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT); | ||
159 | |||
160 | jz4740_timer_set_period(TIMER_CLOCKSOURCE, 0xffff); | ||
161 | |||
162 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
163 | jz4740_timer_enable(TIMER_CLOCKSOURCE); | ||
164 | } | 17 | } |
diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig index fe8db93cf21a..1cb489959a99 100644 --- a/drivers/clk/ingenic/Kconfig +++ b/drivers/clk/ingenic/Kconfig | |||
@@ -1,5 +1,5 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
2 | menu "Ingenic JZ47xx CGU drivers" | 2 | menu "Ingenic SoCs drivers" |
3 | depends on MIPS | 3 | depends on MIPS |
4 | 4 | ||
5 | config INGENIC_CGU_COMMON | 5 | config INGENIC_CGU_COMMON |
@@ -45,4 +45,12 @@ config INGENIC_CGU_JZ4780 | |||
45 | 45 | ||
46 | If building for a JZ4780 SoC, you want to say Y here. | 46 | If building for a JZ4780 SoC, you want to say Y here. |
47 | 47 | ||
48 | config INGENIC_TCU_CLK | ||
49 | bool "Ingenic JZ47xx TCU clocks driver" | ||
50 | default MACH_INGENIC | ||
51 | select MFD_SYSCON | ||
52 | help | ||
53 | Support the clocks of the Timer/Counter Unit (TCU) of the Ingenic | ||
54 | JZ47xx SoCs. | ||
55 | |||
48 | endmenu | 56 | endmenu |
diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile index 250570a809d3..097220b05131 100644 --- a/drivers/clk/ingenic/Makefile +++ b/drivers/clk/ingenic/Makefile | |||
@@ -4,3 +4,4 @@ obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o | |||
4 | obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o | 4 | obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o |
5 | obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o | 5 | obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o |
6 | obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o | 6 | obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o |
7 | obj-$(CONFIG_INGENIC_TCU_CLK) += tcu.o | ||
diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 4c0a20949c2c..67f8a0e14284 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c | |||
@@ -222,6 +222,12 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { | |||
222 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | 222 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, |
223 | .gate = { CGU_REG_CLKGR, 5 }, | 223 | .gate = { CGU_REG_CLKGR, 5 }, |
224 | }, | 224 | }, |
225 | |||
226 | [JZ4740_CLK_TCU] = { | ||
227 | "tcu", CGU_CLK_GATE, | ||
228 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
229 | .gate = { CGU_REG_CLKGR, 1 }, | ||
230 | }, | ||
225 | }; | 231 | }; |
226 | 232 | ||
227 | static void __init jz4740_cgu_init(struct device_node *np) | 233 | static void __init jz4740_cgu_init(struct device_node *np) |
diff --git a/drivers/clk/ingenic/tcu.c b/drivers/clk/ingenic/tcu.c new file mode 100644 index 000000000000..a1a5f9cb439e --- /dev/null +++ b/drivers/clk/ingenic/tcu.c | |||
@@ -0,0 +1,474 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * JZ47xx SoCs TCU clocks driver | ||
4 | * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> | ||
5 | */ | ||
6 | |||
7 | #include <linux/clk.h> | ||
8 | #include <linux/clk-provider.h> | ||
9 | #include <linux/clockchips.h> | ||
10 | #include <linux/mfd/ingenic-tcu.h> | ||
11 | #include <linux/mfd/syscon.h> | ||
12 | #include <linux/regmap.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/syscore_ops.h> | ||
15 | |||
16 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
17 | |||
18 | /* 8 channels max + watchdog + OST */ | ||
19 | #define TCU_CLK_COUNT 10 | ||
20 | |||
21 | #undef pr_fmt | ||
22 | #define pr_fmt(fmt) "ingenic-tcu-clk: " fmt | ||
23 | |||
24 | enum tcu_clk_parent { | ||
25 | TCU_PARENT_PCLK, | ||
26 | TCU_PARENT_RTC, | ||
27 | TCU_PARENT_EXT, | ||
28 | }; | ||
29 | |||
30 | struct ingenic_soc_info { | ||
31 | unsigned int num_channels; | ||
32 | bool has_ost; | ||
33 | bool has_tcu_clk; | ||
34 | }; | ||
35 | |||
36 | struct ingenic_tcu_clk_info { | ||
37 | struct clk_init_data init_data; | ||
38 | u8 gate_bit; | ||
39 | u8 tcsr_reg; | ||
40 | }; | ||
41 | |||
42 | struct ingenic_tcu_clk { | ||
43 | struct clk_hw hw; | ||
44 | unsigned int idx; | ||
45 | struct ingenic_tcu *tcu; | ||
46 | const struct ingenic_tcu_clk_info *info; | ||
47 | }; | ||
48 | |||
49 | struct ingenic_tcu { | ||
50 | const struct ingenic_soc_info *soc_info; | ||
51 | struct regmap *map; | ||
52 | struct clk *clk; | ||
53 | |||
54 | struct clk_hw_onecell_data *clocks; | ||
55 | }; | ||
56 | |||
57 | static struct ingenic_tcu *ingenic_tcu; | ||
58 | |||
59 | static inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw) | ||
60 | { | ||
61 | return container_of(hw, struct ingenic_tcu_clk, hw); | ||
62 | } | ||
63 | |||
64 | static int ingenic_tcu_enable(struct clk_hw *hw) | ||
65 | { | ||
66 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
67 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
68 | struct ingenic_tcu *tcu = tcu_clk->tcu; | ||
69 | |||
70 | regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static void ingenic_tcu_disable(struct clk_hw *hw) | ||
76 | { | ||
77 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
78 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
79 | struct ingenic_tcu *tcu = tcu_clk->tcu; | ||
80 | |||
81 | regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); | ||
82 | } | ||
83 | |||
84 | static int ingenic_tcu_is_enabled(struct clk_hw *hw) | ||
85 | { | ||
86 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
87 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
88 | unsigned int value; | ||
89 | |||
90 | regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value); | ||
91 | |||
92 | return !(value & BIT(info->gate_bit)); | ||
93 | } | ||
94 | |||
95 | static bool ingenic_tcu_enable_regs(struct clk_hw *hw) | ||
96 | { | ||
97 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
98 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
99 | struct ingenic_tcu *tcu = tcu_clk->tcu; | ||
100 | bool enabled = false; | ||
101 | |||
102 | /* | ||
103 | * If the SoC has no global TCU clock, we must ungate the channel's | ||
104 | * clock to be able to access its registers. | ||
105 | * If we have a TCU clock, it will be enabled automatically as it has | ||
106 | * been attached to the regmap. | ||
107 | */ | ||
108 | if (!tcu->clk) { | ||
109 | enabled = !!ingenic_tcu_is_enabled(hw); | ||
110 | regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); | ||
111 | } | ||
112 | |||
113 | return enabled; | ||
114 | } | ||
115 | |||
116 | static void ingenic_tcu_disable_regs(struct clk_hw *hw) | ||
117 | { | ||
118 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
119 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
120 | struct ingenic_tcu *tcu = tcu_clk->tcu; | ||
121 | |||
122 | if (!tcu->clk) | ||
123 | regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); | ||
124 | } | ||
125 | |||
126 | static u8 ingenic_tcu_get_parent(struct clk_hw *hw) | ||
127 | { | ||
128 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
129 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
130 | unsigned int val = 0; | ||
131 | int ret; | ||
132 | |||
133 | ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val); | ||
134 | WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx); | ||
135 | |||
136 | return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1; | ||
137 | } | ||
138 | |||
139 | static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx) | ||
140 | { | ||
141 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
142 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
143 | bool was_enabled; | ||
144 | int ret; | ||
145 | |||
146 | was_enabled = ingenic_tcu_enable_regs(hw); | ||
147 | |||
148 | ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg, | ||
149 | TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx)); | ||
150 | WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx); | ||
151 | |||
152 | if (!was_enabled) | ||
153 | ingenic_tcu_disable_regs(hw); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw, | ||
159 | unsigned long parent_rate) | ||
160 | { | ||
161 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
162 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
163 | unsigned int prescale; | ||
164 | int ret; | ||
165 | |||
166 | ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &prescale); | ||
167 | WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx); | ||
168 | |||
169 | prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB; | ||
170 | |||
171 | return parent_rate >> (prescale * 2); | ||
172 | } | ||
173 | |||
174 | static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate) | ||
175 | { | ||
176 | u8 prescale; | ||
177 | |||
178 | for (prescale = 0; prescale < 5; prescale++) | ||
179 | if ((rate >> (prescale * 2)) <= req_rate) | ||
180 | return prescale; | ||
181 | |||
182 | return 5; /* /1024 divider */ | ||
183 | } | ||
184 | |||
185 | static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate, | ||
186 | unsigned long *parent_rate) | ||
187 | { | ||
188 | unsigned long rate = *parent_rate; | ||
189 | u8 prescale; | ||
190 | |||
191 | if (req_rate > rate) | ||
192 | return -EINVAL; | ||
193 | |||
194 | prescale = ingenic_tcu_get_prescale(rate, req_rate); | ||
195 | |||
196 | return rate >> (prescale * 2); | ||
197 | } | ||
198 | |||
199 | static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate, | ||
200 | unsigned long parent_rate) | ||
201 | { | ||
202 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); | ||
203 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; | ||
204 | u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate); | ||
205 | bool was_enabled; | ||
206 | int ret; | ||
207 | |||
208 | was_enabled = ingenic_tcu_enable_regs(hw); | ||
209 | |||
210 | ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg, | ||
211 | TCU_TCSR_PRESCALE_MASK, | ||
212 | prescale << TCU_TCSR_PRESCALE_LSB); | ||
213 | WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx); | ||
214 | |||
215 | if (!was_enabled) | ||
216 | ingenic_tcu_disable_regs(hw); | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static const struct clk_ops ingenic_tcu_clk_ops = { | ||
222 | .get_parent = ingenic_tcu_get_parent, | ||
223 | .set_parent = ingenic_tcu_set_parent, | ||
224 | |||
225 | .recalc_rate = ingenic_tcu_recalc_rate, | ||
226 | .round_rate = ingenic_tcu_round_rate, | ||
227 | .set_rate = ingenic_tcu_set_rate, | ||
228 | |||
229 | .enable = ingenic_tcu_enable, | ||
230 | .disable = ingenic_tcu_disable, | ||
231 | .is_enabled = ingenic_tcu_is_enabled, | ||
232 | }; | ||
233 | |||
234 | static const char * const ingenic_tcu_timer_parents[] = { | ||
235 | [TCU_PARENT_PCLK] = "pclk", | ||
236 | [TCU_PARENT_RTC] = "rtc", | ||
237 | [TCU_PARENT_EXT] = "ext", | ||
238 | }; | ||
239 | |||
240 | #define DEF_TIMER(_name, _gate_bit, _tcsr) \ | ||
241 | { \ | ||
242 | .init_data = { \ | ||
243 | .name = _name, \ | ||
244 | .parent_names = ingenic_tcu_timer_parents, \ | ||
245 | .num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\ | ||
246 | .ops = &ingenic_tcu_clk_ops, \ | ||
247 | .flags = CLK_SET_RATE_UNGATE, \ | ||
248 | }, \ | ||
249 | .gate_bit = _gate_bit, \ | ||
250 | .tcsr_reg = _tcsr, \ | ||
251 | } | ||
252 | static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = { | ||
253 | [TCU_CLK_TIMER0] = DEF_TIMER("timer0", 0, TCU_REG_TCSRc(0)), | ||
254 | [TCU_CLK_TIMER1] = DEF_TIMER("timer1", 1, TCU_REG_TCSRc(1)), | ||
255 | [TCU_CLK_TIMER2] = DEF_TIMER("timer2", 2, TCU_REG_TCSRc(2)), | ||
256 | [TCU_CLK_TIMER3] = DEF_TIMER("timer3", 3, TCU_REG_TCSRc(3)), | ||
257 | [TCU_CLK_TIMER4] = DEF_TIMER("timer4", 4, TCU_REG_TCSRc(4)), | ||
258 | [TCU_CLK_TIMER5] = DEF_TIMER("timer5", 5, TCU_REG_TCSRc(5)), | ||
259 | [TCU_CLK_TIMER6] = DEF_TIMER("timer6", 6, TCU_REG_TCSRc(6)), | ||
260 | [TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)), | ||
261 | }; | ||
262 | |||
263 | static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info = | ||
264 | DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR); | ||
265 | static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info = | ||
266 | DEF_TIMER("ost", 15, TCU_REG_OST_TCSR); | ||
267 | #undef DEF_TIMER | ||
268 | |||
269 | static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu, | ||
270 | unsigned int idx, enum tcu_clk_parent parent, | ||
271 | const struct ingenic_tcu_clk_info *info, | ||
272 | struct clk_hw_onecell_data *clocks) | ||
273 | { | ||
274 | struct ingenic_tcu_clk *tcu_clk; | ||
275 | int err; | ||
276 | |||
277 | tcu_clk = kzalloc(sizeof(*tcu_clk), GFP_KERNEL); | ||
278 | if (!tcu_clk) | ||
279 | return -ENOMEM; | ||
280 | |||
281 | tcu_clk->hw.init = &info->init_data; | ||
282 | tcu_clk->idx = idx; | ||
283 | tcu_clk->info = info; | ||
284 | tcu_clk->tcu = tcu; | ||
285 | |||
286 | /* Reset channel and clock divider, set default parent */ | ||
287 | ingenic_tcu_enable_regs(&tcu_clk->hw); | ||
288 | regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent)); | ||
289 | ingenic_tcu_disable_regs(&tcu_clk->hw); | ||
290 | |||
291 | err = clk_hw_register(NULL, &tcu_clk->hw); | ||
292 | if (err) { | ||
293 | kfree(tcu_clk); | ||
294 | return err; | ||
295 | } | ||
296 | |||
297 | clocks->hws[idx] = &tcu_clk->hw; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static const struct ingenic_soc_info jz4740_soc_info = { | ||
303 | .num_channels = 8, | ||
304 | .has_ost = false, | ||
305 | .has_tcu_clk = true, | ||
306 | }; | ||
307 | |||
308 | static const struct ingenic_soc_info jz4725b_soc_info = { | ||
309 | .num_channels = 6, | ||
310 | .has_ost = true, | ||
311 | .has_tcu_clk = true, | ||
312 | }; | ||
313 | |||
314 | static const struct ingenic_soc_info jz4770_soc_info = { | ||
315 | .num_channels = 8, | ||
316 | .has_ost = true, | ||
317 | .has_tcu_clk = false, | ||
318 | }; | ||
319 | |||
320 | static const struct of_device_id ingenic_tcu_of_match[] __initconst = { | ||
321 | { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, }, | ||
322 | { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, }, | ||
323 | { .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, }, | ||
324 | { /* sentinel */ } | ||
325 | }; | ||
326 | |||
327 | static int __init ingenic_tcu_probe(struct device_node *np) | ||
328 | { | ||
329 | const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np); | ||
330 | struct ingenic_tcu *tcu; | ||
331 | struct regmap *map; | ||
332 | unsigned int i; | ||
333 | int ret; | ||
334 | |||
335 | map = device_node_to_regmap(np); | ||
336 | if (IS_ERR(map)) | ||
337 | return PTR_ERR(map); | ||
338 | |||
339 | tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); | ||
340 | if (!tcu) | ||
341 | return -ENOMEM; | ||
342 | |||
343 | tcu->map = map; | ||
344 | tcu->soc_info = id->data; | ||
345 | |||
346 | if (tcu->soc_info->has_tcu_clk) { | ||
347 | tcu->clk = of_clk_get_by_name(np, "tcu"); | ||
348 | if (IS_ERR(tcu->clk)) { | ||
349 | ret = PTR_ERR(tcu->clk); | ||
350 | pr_crit("Cannot get TCU clock\n"); | ||
351 | goto err_free_tcu; | ||
352 | } | ||
353 | |||
354 | ret = clk_prepare_enable(tcu->clk); | ||
355 | if (ret) { | ||
356 | pr_crit("Unable to enable TCU clock\n"); | ||
357 | goto err_put_clk; | ||
358 | } | ||
359 | } | ||
360 | |||
361 | tcu->clocks = kzalloc(sizeof(*tcu->clocks) + | ||
362 | sizeof(*tcu->clocks->hws) * TCU_CLK_COUNT, | ||
363 | GFP_KERNEL); | ||
364 | if (!tcu->clocks) { | ||
365 | ret = -ENOMEM; | ||
366 | goto err_clk_disable; | ||
367 | } | ||
368 | |||
369 | tcu->clocks->num = TCU_CLK_COUNT; | ||
370 | |||
371 | for (i = 0; i < tcu->soc_info->num_channels; i++) { | ||
372 | ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT, | ||
373 | &ingenic_tcu_clk_info[i], | ||
374 | tcu->clocks); | ||
375 | if (ret) { | ||
376 | pr_crit("cannot register clock %d\n", i); | ||
377 | goto err_unregister_timer_clocks; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * We set EXT as the default parent clock for all the TCU clocks | ||
383 | * except for the watchdog one, where we set the RTC clock as the | ||
384 | * parent. Since the EXT and PCLK are much faster than the RTC clock, | ||
385 | * the watchdog would kick after a maximum time of 5s, and we might | ||
386 | * want a slower kicking time. | ||
387 | */ | ||
388 | ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC, | ||
389 | &ingenic_tcu_watchdog_clk_info, | ||
390 | tcu->clocks); | ||
391 | if (ret) { | ||
392 | pr_crit("cannot register watchdog clock\n"); | ||
393 | goto err_unregister_timer_clocks; | ||
394 | } | ||
395 | |||
396 | if (tcu->soc_info->has_ost) { | ||
397 | ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST, | ||
398 | TCU_PARENT_EXT, | ||
399 | &ingenic_tcu_ost_clk_info, | ||
400 | tcu->clocks); | ||
401 | if (ret) { | ||
402 | pr_crit("cannot register ost clock\n"); | ||
403 | goto err_unregister_watchdog_clock; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks); | ||
408 | if (ret) { | ||
409 | pr_crit("cannot add OF clock provider\n"); | ||
410 | goto err_unregister_ost_clock; | ||
411 | } | ||
412 | |||
413 | ingenic_tcu = tcu; | ||
414 | |||
415 | return 0; | ||
416 | |||
417 | err_unregister_ost_clock: | ||
418 | if (tcu->soc_info->has_ost) | ||
419 | clk_hw_unregister(tcu->clocks->hws[i + 1]); | ||
420 | err_unregister_watchdog_clock: | ||
421 | clk_hw_unregister(tcu->clocks->hws[i]); | ||
422 | err_unregister_timer_clocks: | ||
423 | for (i = 0; i < tcu->clocks->num; i++) | ||
424 | if (tcu->clocks->hws[i]) | ||
425 | clk_hw_unregister(tcu->clocks->hws[i]); | ||
426 | kfree(tcu->clocks); | ||
427 | err_clk_disable: | ||
428 | if (tcu->soc_info->has_tcu_clk) | ||
429 | clk_disable_unprepare(tcu->clk); | ||
430 | err_put_clk: | ||
431 | if (tcu->soc_info->has_tcu_clk) | ||
432 | clk_put(tcu->clk); | ||
433 | err_free_tcu: | ||
434 | kfree(tcu); | ||
435 | return ret; | ||
436 | } | ||
437 | |||
438 | static int __maybe_unused tcu_pm_suspend(void) | ||
439 | { | ||
440 | struct ingenic_tcu *tcu = ingenic_tcu; | ||
441 | |||
442 | if (tcu->clk) | ||
443 | clk_disable(tcu->clk); | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static void __maybe_unused tcu_pm_resume(void) | ||
449 | { | ||
450 | struct ingenic_tcu *tcu = ingenic_tcu; | ||
451 | |||
452 | if (tcu->clk) | ||
453 | clk_enable(tcu->clk); | ||
454 | } | ||
455 | |||
456 | static struct syscore_ops __maybe_unused tcu_pm_ops = { | ||
457 | .suspend = tcu_pm_suspend, | ||
458 | .resume = tcu_pm_resume, | ||
459 | }; | ||
460 | |||
461 | static void __init ingenic_tcu_init(struct device_node *np) | ||
462 | { | ||
463 | int ret = ingenic_tcu_probe(np); | ||
464 | |||
465 | if (ret) | ||
466 | pr_crit("Failed to initialize TCU clocks: %d\n", ret); | ||
467 | |||
468 | if (IS_ENABLED(CONFIG_PM_SLEEP)) | ||
469 | register_syscore_ops(&tcu_pm_ops); | ||
470 | } | ||
471 | |||
472 | CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init); | ||
473 | CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init); | ||
474 | CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init); | ||
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 5e9317dc3d39..a9cdc2c4f8bd 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -685,4 +685,15 @@ config MILBEAUT_TIMER | |||
685 | help | 685 | help |
686 | Enables the support for Milbeaut timer driver. | 686 | Enables the support for Milbeaut timer driver. |
687 | 687 | ||
688 | config INGENIC_TIMER | ||
689 | bool "Clocksource/timer using the TCU in Ingenic JZ SoCs" | ||
690 | default MACH_INGENIC | ||
691 | depends on MIPS || COMPILE_TEST | ||
692 | depends on COMMON_CLK | ||
693 | select MFD_SYSCON | ||
694 | select TIMER_OF | ||
695 | select IRQ_DOMAIN | ||
696 | help | ||
697 | Support for the timer/counter unit of the Ingenic JZ SoCs. | ||
698 | |||
688 | endmenu | 699 | endmenu |
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 2e7936e7833f..4dfe4225ece7 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
@@ -80,6 +80,7 @@ obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o | |||
80 | obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o | 80 | obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o |
81 | obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o | 81 | obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o |
82 | obj-$(CONFIG_H8300_TPU) += h8300_tpu.o | 82 | obj-$(CONFIG_H8300_TPU) += h8300_tpu.o |
83 | obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o | ||
83 | obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o | 84 | obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o |
84 | obj-$(CONFIG_X86_NUMACHIP) += numachip.o | 85 | obj-$(CONFIG_X86_NUMACHIP) += numachip.o |
85 | obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o | 86 | obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o |
diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c new file mode 100644 index 000000000000..4bbdb3d3d0c6 --- /dev/null +++ b/drivers/clocksource/ingenic-timer.c | |||
@@ -0,0 +1,356 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * JZ47xx SoCs TCU IRQ driver | ||
4 | * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> | ||
5 | */ | ||
6 | |||
7 | #include <linux/bitops.h> | ||
8 | #include <linux/clk.h> | ||
9 | #include <linux/clockchips.h> | ||
10 | #include <linux/clocksource.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/mfd/ingenic-tcu.h> | ||
13 | #include <linux/mfd/syscon.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/of_irq.h> | ||
17 | #include <linux/of_platform.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/regmap.h> | ||
20 | #include <linux/sched_clock.h> | ||
21 | |||
22 | #include <dt-bindings/clock/ingenic,tcu.h> | ||
23 | |||
24 | struct ingenic_soc_info { | ||
25 | unsigned int num_channels; | ||
26 | }; | ||
27 | |||
28 | struct ingenic_tcu { | ||
29 | struct regmap *map; | ||
30 | struct clk *timer_clk, *cs_clk; | ||
31 | unsigned int timer_channel, cs_channel; | ||
32 | struct clock_event_device cevt; | ||
33 | struct clocksource cs; | ||
34 | char name[4]; | ||
35 | unsigned long pwm_channels_mask; | ||
36 | }; | ||
37 | |||
38 | static struct ingenic_tcu *ingenic_tcu; | ||
39 | |||
40 | static u64 notrace ingenic_tcu_timer_read(void) | ||
41 | { | ||
42 | struct ingenic_tcu *tcu = ingenic_tcu; | ||
43 | unsigned int count; | ||
44 | |||
45 | regmap_read(tcu->map, TCU_REG_TCNTc(tcu->cs_channel), &count); | ||
46 | |||
47 | return count; | ||
48 | } | ||
49 | |||
50 | static u64 notrace ingenic_tcu_timer_cs_read(struct clocksource *cs) | ||
51 | { | ||
52 | return ingenic_tcu_timer_read(); | ||
53 | } | ||
54 | |||
55 | static inline struct ingenic_tcu *to_ingenic_tcu(struct clock_event_device *evt) | ||
56 | { | ||
57 | return container_of(evt, struct ingenic_tcu, cevt); | ||
58 | } | ||
59 | |||
60 | static int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt) | ||
61 | { | ||
62 | struct ingenic_tcu *tcu = to_ingenic_tcu(evt); | ||
63 | |||
64 | regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel)); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int ingenic_tcu_cevt_set_next(unsigned long next, | ||
70 | struct clock_event_device *evt) | ||
71 | { | ||
72 | struct ingenic_tcu *tcu = to_ingenic_tcu(evt); | ||
73 | |||
74 | if (next > 0xffff) | ||
75 | return -EINVAL; | ||
76 | |||
77 | regmap_write(tcu->map, TCU_REG_TDFRc(tcu->timer_channel), next); | ||
78 | regmap_write(tcu->map, TCU_REG_TCNTc(tcu->timer_channel), 0); | ||
79 | regmap_write(tcu->map, TCU_REG_TESR, BIT(tcu->timer_channel)); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id) | ||
85 | { | ||
86 | struct clock_event_device *evt = dev_id; | ||
87 | struct ingenic_tcu *tcu = to_ingenic_tcu(evt); | ||
88 | |||
89 | regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel)); | ||
90 | |||
91 | if (evt->event_handler) | ||
92 | evt->event_handler(evt); | ||
93 | |||
94 | return IRQ_HANDLED; | ||
95 | } | ||
96 | |||
97 | static struct clk * __init ingenic_tcu_get_clock(struct device_node *np, int id) | ||
98 | { | ||
99 | struct of_phandle_args args; | ||
100 | |||
101 | args.np = np; | ||
102 | args.args_count = 1; | ||
103 | args.args[0] = id; | ||
104 | |||
105 | return of_clk_get_from_provider(&args); | ||
106 | } | ||
107 | |||
108 | static int __init ingenic_tcu_timer_init(struct device_node *np, | ||
109 | struct ingenic_tcu *tcu) | ||
110 | { | ||
111 | unsigned int timer_virq, channel = tcu->timer_channel; | ||
112 | struct irq_domain *domain; | ||
113 | unsigned long rate; | ||
114 | int err; | ||
115 | |||
116 | tcu->timer_clk = ingenic_tcu_get_clock(np, channel); | ||
117 | if (IS_ERR(tcu->timer_clk)) | ||
118 | return PTR_ERR(tcu->timer_clk); | ||
119 | |||
120 | err = clk_prepare_enable(tcu->timer_clk); | ||
121 | if (err) | ||
122 | goto err_clk_put; | ||
123 | |||
124 | rate = clk_get_rate(tcu->timer_clk); | ||
125 | if (!rate) { | ||
126 | err = -EINVAL; | ||
127 | goto err_clk_disable; | ||
128 | } | ||
129 | |||
130 | domain = irq_find_host(np); | ||
131 | if (!domain) { | ||
132 | err = -ENODEV; | ||
133 | goto err_clk_disable; | ||
134 | } | ||
135 | |||
136 | timer_virq = irq_create_mapping(domain, channel); | ||
137 | if (!timer_virq) { | ||
138 | err = -EINVAL; | ||
139 | goto err_clk_disable; | ||
140 | } | ||
141 | |||
142 | snprintf(tcu->name, sizeof(tcu->name), "TCU"); | ||
143 | |||
144 | err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER, | ||
145 | tcu->name, &tcu->cevt); | ||
146 | if (err) | ||
147 | goto err_irq_dispose_mapping; | ||
148 | |||
149 | tcu->cevt.cpumask = cpumask_of(smp_processor_id()); | ||
150 | tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT; | ||
151 | tcu->cevt.name = tcu->name; | ||
152 | tcu->cevt.rating = 200; | ||
153 | tcu->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown; | ||
154 | tcu->cevt.set_next_event = ingenic_tcu_cevt_set_next; | ||
155 | |||
156 | clockevents_config_and_register(&tcu->cevt, rate, 10, 0xffff); | ||
157 | |||
158 | return 0; | ||
159 | |||
160 | err_irq_dispose_mapping: | ||
161 | irq_dispose_mapping(timer_virq); | ||
162 | err_clk_disable: | ||
163 | clk_disable_unprepare(tcu->timer_clk); | ||
164 | err_clk_put: | ||
165 | clk_put(tcu->timer_clk); | ||
166 | return err; | ||
167 | } | ||
168 | |||
169 | static int __init ingenic_tcu_clocksource_init(struct device_node *np, | ||
170 | struct ingenic_tcu *tcu) | ||
171 | { | ||
172 | unsigned int channel = tcu->cs_channel; | ||
173 | struct clocksource *cs = &tcu->cs; | ||
174 | unsigned long rate; | ||
175 | int err; | ||
176 | |||
177 | tcu->cs_clk = ingenic_tcu_get_clock(np, channel); | ||
178 | if (IS_ERR(tcu->cs_clk)) | ||
179 | return PTR_ERR(tcu->cs_clk); | ||
180 | |||
181 | err = clk_prepare_enable(tcu->cs_clk); | ||
182 | if (err) | ||
183 | goto err_clk_put; | ||
184 | |||
185 | rate = clk_get_rate(tcu->cs_clk); | ||
186 | if (!rate) { | ||
187 | err = -EINVAL; | ||
188 | goto err_clk_disable; | ||
189 | } | ||
190 | |||
191 | /* Reset channel */ | ||
192 | regmap_update_bits(tcu->map, TCU_REG_TCSRc(channel), | ||
193 | 0xffff & ~TCU_TCSR_RESERVED_BITS, 0); | ||
194 | |||
195 | /* Reset counter */ | ||
196 | regmap_write(tcu->map, TCU_REG_TDFRc(channel), 0xffff); | ||
197 | regmap_write(tcu->map, TCU_REG_TCNTc(channel), 0); | ||
198 | |||
199 | /* Enable channel */ | ||
200 | regmap_write(tcu->map, TCU_REG_TESR, BIT(channel)); | ||
201 | |||
202 | cs->name = "ingenic-timer"; | ||
203 | cs->rating = 200; | ||
204 | cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; | ||
205 | cs->mask = CLOCKSOURCE_MASK(16); | ||
206 | cs->read = ingenic_tcu_timer_cs_read; | ||
207 | |||
208 | err = clocksource_register_hz(cs, rate); | ||
209 | if (err) | ||
210 | goto err_clk_disable; | ||
211 | |||
212 | return 0; | ||
213 | |||
214 | err_clk_disable: | ||
215 | clk_disable_unprepare(tcu->cs_clk); | ||
216 | err_clk_put: | ||
217 | clk_put(tcu->cs_clk); | ||
218 | return err; | ||
219 | } | ||
220 | |||
221 | static const struct ingenic_soc_info jz4740_soc_info = { | ||
222 | .num_channels = 8, | ||
223 | }; | ||
224 | |||
225 | static const struct ingenic_soc_info jz4725b_soc_info = { | ||
226 | .num_channels = 6, | ||
227 | }; | ||
228 | |||
229 | static const struct of_device_id ingenic_tcu_of_match[] = { | ||
230 | { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, }, | ||
231 | { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, }, | ||
232 | { .compatible = "ingenic,jz4770-tcu", .data = &jz4740_soc_info, }, | ||
233 | { /* sentinel */ } | ||
234 | }; | ||
235 | |||
236 | static int __init ingenic_tcu_init(struct device_node *np) | ||
237 | { | ||
238 | const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np); | ||
239 | const struct ingenic_soc_info *soc_info = id->data; | ||
240 | struct ingenic_tcu *tcu; | ||
241 | struct regmap *map; | ||
242 | long rate; | ||
243 | int ret; | ||
244 | |||
245 | of_node_clear_flag(np, OF_POPULATED); | ||
246 | |||
247 | map = device_node_to_regmap(np); | ||
248 | if (IS_ERR(map)) | ||
249 | return PTR_ERR(map); | ||
250 | |||
251 | tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); | ||
252 | if (!tcu) | ||
253 | return -ENOMEM; | ||
254 | |||
255 | /* Enable all TCU channels for PWM use by default except channels 0/1 */ | ||
256 | tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2); | ||
257 | of_property_read_u32(np, "ingenic,pwm-channels-mask", | ||
258 | (u32 *)&tcu->pwm_channels_mask); | ||
259 | |||
260 | /* Verify that we have at least two free channels */ | ||
261 | if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) { | ||
262 | pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__, | ||
263 | tcu->pwm_channels_mask); | ||
264 | ret = -EINVAL; | ||
265 | goto err_free_ingenic_tcu; | ||
266 | } | ||
267 | |||
268 | tcu->map = map; | ||
269 | ingenic_tcu = tcu; | ||
270 | |||
271 | tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask, | ||
272 | soc_info->num_channels); | ||
273 | tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask, | ||
274 | soc_info->num_channels, | ||
275 | tcu->timer_channel + 1); | ||
276 | |||
277 | ret = ingenic_tcu_clocksource_init(np, tcu); | ||
278 | if (ret) { | ||
279 | pr_crit("%s: Unable to init clocksource: %d\n", __func__, ret); | ||
280 | goto err_free_ingenic_tcu; | ||
281 | } | ||
282 | |||
283 | ret = ingenic_tcu_timer_init(np, tcu); | ||
284 | if (ret) | ||
285 | goto err_tcu_clocksource_cleanup; | ||
286 | |||
287 | /* Register the sched_clock at the end as there's no way to undo it */ | ||
288 | rate = clk_get_rate(tcu->cs_clk); | ||
289 | sched_clock_register(ingenic_tcu_timer_read, 16, rate); | ||
290 | |||
291 | return 0; | ||
292 | |||
293 | err_tcu_clocksource_cleanup: | ||
294 | clocksource_unregister(&tcu->cs); | ||
295 | clk_disable_unprepare(tcu->cs_clk); | ||
296 | clk_put(tcu->cs_clk); | ||
297 | err_free_ingenic_tcu: | ||
298 | kfree(tcu); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | TIMER_OF_DECLARE(jz4740_tcu_intc, "ingenic,jz4740-tcu", ingenic_tcu_init); | ||
303 | TIMER_OF_DECLARE(jz4725b_tcu_intc, "ingenic,jz4725b-tcu", ingenic_tcu_init); | ||
304 | TIMER_OF_DECLARE(jz4770_tcu_intc, "ingenic,jz4770-tcu", ingenic_tcu_init); | ||
305 | |||
306 | |||
307 | static int __init ingenic_tcu_probe(struct platform_device *pdev) | ||
308 | { | ||
309 | platform_set_drvdata(pdev, ingenic_tcu); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static int __maybe_unused ingenic_tcu_suspend(struct device *dev) | ||
315 | { | ||
316 | struct ingenic_tcu *tcu = dev_get_drvdata(dev); | ||
317 | |||
318 | clk_disable(tcu->cs_clk); | ||
319 | clk_disable(tcu->timer_clk); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int __maybe_unused ingenic_tcu_resume(struct device *dev) | ||
324 | { | ||
325 | struct ingenic_tcu *tcu = dev_get_drvdata(dev); | ||
326 | int ret; | ||
327 | |||
328 | ret = clk_enable(tcu->timer_clk); | ||
329 | if (ret) | ||
330 | return ret; | ||
331 | |||
332 | ret = clk_enable(tcu->cs_clk); | ||
333 | if (ret) { | ||
334 | clk_disable(tcu->timer_clk); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = { | ||
342 | /* _noirq: We want the TCU clocks to be gated last / ungated first */ | ||
343 | .suspend_noirq = ingenic_tcu_suspend, | ||
344 | .resume_noirq = ingenic_tcu_resume, | ||
345 | }; | ||
346 | |||
347 | static struct platform_driver ingenic_tcu_driver = { | ||
348 | .driver = { | ||
349 | .name = "ingenic-tcu-timer", | ||
350 | #ifdef CONFIG_PM_SLEEP | ||
351 | .pm = &ingenic_tcu_pm_ops, | ||
352 | #endif | ||
353 | .of_match_table = ingenic_tcu_of_match, | ||
354 | }, | ||
355 | }; | ||
356 | builtin_platform_driver_probe(ingenic_tcu_driver, ingenic_tcu_probe); | ||
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 80e10f4e213a..3c8308e6b3a7 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -315,6 +315,17 @@ config INGENIC_IRQ | |||
315 | depends on MACH_INGENIC | 315 | depends on MACH_INGENIC |
316 | default y | 316 | default y |
317 | 317 | ||
318 | config INGENIC_TCU_IRQ | ||
319 | bool "Ingenic JZ47xx TCU interrupt controller" | ||
320 | default MACH_INGENIC | ||
321 | depends on MIPS || COMPILE_TEST | ||
322 | select MFD_SYSCON | ||
323 | help | ||
324 | Support for interrupts in the Timer/Counter Unit (TCU) of the Ingenic | ||
325 | JZ47xx SoCs. | ||
326 | |||
327 | If unsure, say N. | ||
328 | |||
318 | config RENESAS_H8300H_INTC | 329 | config RENESAS_H8300H_INTC |
319 | bool | 330 | bool |
320 | select IRQ_DOMAIN | 331 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 8d0fcec6ab23..cc7c43932f16 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -75,6 +75,7 @@ obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o | |||
75 | obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o | 75 | obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o |
76 | obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o | 76 | obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o |
77 | obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o | 77 | obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o |
78 | obj-$(CONFIG_INGENIC_TCU_IRQ) += irq-ingenic-tcu.o | ||
78 | obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o | 79 | obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o |
79 | obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o | 80 | obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o |
80 | obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o | 81 | obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o |
diff --git a/drivers/irqchip/irq-ingenic-tcu.c b/drivers/irqchip/irq-ingenic-tcu.c new file mode 100644 index 000000000000..6d05cefe9d79 --- /dev/null +++ b/drivers/irqchip/irq-ingenic-tcu.c | |||
@@ -0,0 +1,182 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * JZ47xx SoCs TCU IRQ driver | ||
4 | * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> | ||
5 | */ | ||
6 | |||
7 | #include <linux/clk.h> | ||
8 | #include <linux/interrupt.h> | ||
9 | #include <linux/irqchip.h> | ||
10 | #include <linux/irqchip/chained_irq.h> | ||
11 | #include <linux/mfd/ingenic-tcu.h> | ||
12 | #include <linux/mfd/syscon.h> | ||
13 | #include <linux/of_irq.h> | ||
14 | #include <linux/regmap.h> | ||
15 | |||
16 | struct ingenic_tcu { | ||
17 | struct regmap *map; | ||
18 | struct clk *clk; | ||
19 | struct irq_domain *domain; | ||
20 | unsigned int nb_parent_irqs; | ||
21 | u32 parent_irqs[3]; | ||
22 | }; | ||
23 | |||
24 | static void ingenic_tcu_intc_cascade(struct irq_desc *desc) | ||
25 | { | ||
26 | struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data); | ||
27 | struct irq_domain *domain = irq_desc_get_handler_data(desc); | ||
28 | struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); | ||
29 | struct regmap *map = gc->private; | ||
30 | uint32_t irq_reg, irq_mask; | ||
31 | unsigned int i; | ||
32 | |||
33 | regmap_read(map, TCU_REG_TFR, &irq_reg); | ||
34 | regmap_read(map, TCU_REG_TMR, &irq_mask); | ||
35 | |||
36 | chained_irq_enter(irq_chip, desc); | ||
37 | |||
38 | irq_reg &= ~irq_mask; | ||
39 | |||
40 | for_each_set_bit(i, (unsigned long *)&irq_reg, 32) | ||
41 | generic_handle_irq(irq_linear_revmap(domain, i)); | ||
42 | |||
43 | chained_irq_exit(irq_chip, desc); | ||
44 | } | ||
45 | |||
46 | static void ingenic_tcu_gc_unmask_enable_reg(struct irq_data *d) | ||
47 | { | ||
48 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
49 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
50 | struct regmap *map = gc->private; | ||
51 | u32 mask = d->mask; | ||
52 | |||
53 | irq_gc_lock(gc); | ||
54 | regmap_write(map, ct->regs.ack, mask); | ||
55 | regmap_write(map, ct->regs.enable, mask); | ||
56 | *ct->mask_cache |= mask; | ||
57 | irq_gc_unlock(gc); | ||
58 | } | ||
59 | |||
60 | static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) | ||
61 | { | ||
62 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
63 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
64 | struct regmap *map = gc->private; | ||
65 | u32 mask = d->mask; | ||
66 | |||
67 | irq_gc_lock(gc); | ||
68 | regmap_write(map, ct->regs.disable, mask); | ||
69 | *ct->mask_cache &= ~mask; | ||
70 | irq_gc_unlock(gc); | ||
71 | } | ||
72 | |||
73 | static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) | ||
74 | { | ||
75 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
76 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
77 | struct regmap *map = gc->private; | ||
78 | u32 mask = d->mask; | ||
79 | |||
80 | irq_gc_lock(gc); | ||
81 | regmap_write(map, ct->regs.ack, mask); | ||
82 | regmap_write(map, ct->regs.disable, mask); | ||
83 | irq_gc_unlock(gc); | ||
84 | } | ||
85 | |||
86 | static int __init ingenic_tcu_irq_init(struct device_node *np, | ||
87 | struct device_node *parent) | ||
88 | { | ||
89 | struct irq_chip_generic *gc; | ||
90 | struct irq_chip_type *ct; | ||
91 | struct ingenic_tcu *tcu; | ||
92 | struct regmap *map; | ||
93 | unsigned int i; | ||
94 | int ret, irqs; | ||
95 | |||
96 | map = device_node_to_regmap(np); | ||
97 | if (IS_ERR(map)) | ||
98 | return PTR_ERR(map); | ||
99 | |||
100 | tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); | ||
101 | if (!tcu) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | tcu->map = map; | ||
105 | |||
106 | irqs = of_property_count_elems_of_size(np, "interrupts", sizeof(u32)); | ||
107 | if (irqs < 0 || irqs > ARRAY_SIZE(tcu->parent_irqs)) { | ||
108 | pr_crit("%s: Invalid 'interrupts' property\n", __func__); | ||
109 | ret = -EINVAL; | ||
110 | goto err_free_tcu; | ||
111 | } | ||
112 | |||
113 | tcu->nb_parent_irqs = irqs; | ||
114 | |||
115 | tcu->domain = irq_domain_add_linear(np, 32, &irq_generic_chip_ops, | ||
116 | NULL); | ||
117 | if (!tcu->domain) { | ||
118 | ret = -ENOMEM; | ||
119 | goto err_free_tcu; | ||
120 | } | ||
121 | |||
122 | ret = irq_alloc_domain_generic_chips(tcu->domain, 32, 1, "TCU", | ||
123 | handle_level_irq, 0, | ||
124 | IRQ_NOPROBE | IRQ_LEVEL, 0); | ||
125 | if (ret) { | ||
126 | pr_crit("%s: Invalid 'interrupts' property\n", __func__); | ||
127 | goto out_domain_remove; | ||
128 | } | ||
129 | |||
130 | gc = irq_get_domain_generic_chip(tcu->domain, 0); | ||
131 | ct = gc->chip_types; | ||
132 | |||
133 | gc->wake_enabled = IRQ_MSK(32); | ||
134 | gc->private = tcu->map; | ||
135 | |||
136 | ct->regs.disable = TCU_REG_TMSR; | ||
137 | ct->regs.enable = TCU_REG_TMCR; | ||
138 | ct->regs.ack = TCU_REG_TFCR; | ||
139 | ct->chip.irq_unmask = ingenic_tcu_gc_unmask_enable_reg; | ||
140 | ct->chip.irq_mask = ingenic_tcu_gc_mask_disable_reg; | ||
141 | ct->chip.irq_mask_ack = ingenic_tcu_gc_mask_disable_reg_and_ack; | ||
142 | ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; | ||
143 | |||
144 | /* Mask all IRQs by default */ | ||
145 | regmap_write(tcu->map, TCU_REG_TMSR, IRQ_MSK(32)); | ||
146 | |||
147 | /* | ||
148 | * On JZ4740, timer 0 and timer 1 have their own interrupt line; | ||
149 | * timers 2-7 share one interrupt. | ||
150 | * On SoCs >= JZ4770, timer 5 has its own interrupt line; | ||
151 | * timers 0-4 and 6-7 share one single interrupt. | ||
152 | * | ||
153 | * To keep things simple, we just register the same handler to | ||
154 | * all parent interrupts. The handler will properly detect which | ||
155 | * channel fired the interrupt. | ||
156 | */ | ||
157 | for (i = 0; i < irqs; i++) { | ||
158 | tcu->parent_irqs[i] = irq_of_parse_and_map(np, i); | ||
159 | if (!tcu->parent_irqs[i]) { | ||
160 | ret = -EINVAL; | ||
161 | goto out_unmap_irqs; | ||
162 | } | ||
163 | |||
164 | irq_set_chained_handler_and_data(tcu->parent_irqs[i], | ||
165 | ingenic_tcu_intc_cascade, | ||
166 | tcu->domain); | ||
167 | } | ||
168 | |||
169 | return 0; | ||
170 | |||
171 | out_unmap_irqs: | ||
172 | for (; i > 0; i--) | ||
173 | irq_dispose_mapping(tcu->parent_irqs[i - 1]); | ||
174 | out_domain_remove: | ||
175 | irq_domain_remove(tcu->domain); | ||
176 | err_free_tcu: | ||
177 | kfree(tcu); | ||
178 | return ret; | ||
179 | } | ||
180 | IRQCHIP_DECLARE(jz4740_tcu_irq, "ingenic,jz4740-tcu", ingenic_tcu_irq_init); | ||
181 | IRQCHIP_DECLARE(jz4725b_tcu_irq, "ingenic,jz4725b-tcu", ingenic_tcu_irq_init); | ||
182 | IRQCHIP_DECLARE(jz4770_tcu_irq, "ingenic,jz4770-tcu", ingenic_tcu_irq_init); | ||
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index b65e585fc8c6..660723276481 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c | |||
@@ -40,7 +40,7 @@ static const struct regmap_config syscon_regmap_config = { | |||
40 | .reg_stride = 4, | 40 | .reg_stride = 4, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static struct syscon *of_syscon_register(struct device_node *np) | 43 | static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) |
44 | { | 44 | { |
45 | struct clk *clk; | 45 | struct clk *clk; |
46 | struct syscon *syscon; | 46 | struct syscon *syscon; |
@@ -51,9 +51,6 @@ static struct syscon *of_syscon_register(struct device_node *np) | |||
51 | struct regmap_config syscon_config = syscon_regmap_config; | 51 | struct regmap_config syscon_config = syscon_regmap_config; |
52 | struct resource res; | 52 | struct resource res; |
53 | 53 | ||
54 | if (!of_device_is_compatible(np, "syscon")) | ||
55 | return ERR_PTR(-EINVAL); | ||
56 | |||
57 | syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); | 54 | syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); |
58 | if (!syscon) | 55 | if (!syscon) |
59 | return ERR_PTR(-ENOMEM); | 56 | return ERR_PTR(-ENOMEM); |
@@ -117,16 +114,18 @@ static struct syscon *of_syscon_register(struct device_node *np) | |||
117 | goto err_regmap; | 114 | goto err_regmap; |
118 | } | 115 | } |
119 | 116 | ||
120 | clk = of_clk_get(np, 0); | 117 | if (check_clk) { |
121 | if (IS_ERR(clk)) { | 118 | clk = of_clk_get(np, 0); |
122 | ret = PTR_ERR(clk); | 119 | if (IS_ERR(clk)) { |
123 | /* clock is optional */ | 120 | ret = PTR_ERR(clk); |
124 | if (ret != -ENOENT) | 121 | /* clock is optional */ |
125 | goto err_clk; | 122 | if (ret != -ENOENT) |
126 | } else { | 123 | goto err_clk; |
127 | ret = regmap_mmio_attach_clk(regmap, clk); | 124 | } else { |
128 | if (ret) | 125 | ret = regmap_mmio_attach_clk(regmap, clk); |
129 | goto err_attach; | 126 | if (ret) |
127 | goto err_attach; | ||
128 | } | ||
130 | } | 129 | } |
131 | 130 | ||
132 | syscon->regmap = regmap; | 131 | syscon->regmap = regmap; |
@@ -150,7 +149,8 @@ err_map: | |||
150 | return ERR_PTR(ret); | 149 | return ERR_PTR(ret); |
151 | } | 150 | } |
152 | 151 | ||
153 | struct regmap *syscon_node_to_regmap(struct device_node *np) | 152 | static struct regmap *device_node_get_regmap(struct device_node *np, |
153 | bool check_clk) | ||
154 | { | 154 | { |
155 | struct syscon *entry, *syscon = NULL; | 155 | struct syscon *entry, *syscon = NULL; |
156 | 156 | ||
@@ -165,13 +165,27 @@ struct regmap *syscon_node_to_regmap(struct device_node *np) | |||
165 | spin_unlock(&syscon_list_slock); | 165 | spin_unlock(&syscon_list_slock); |
166 | 166 | ||
167 | if (!syscon) | 167 | if (!syscon) |
168 | syscon = of_syscon_register(np); | 168 | syscon = of_syscon_register(np, check_clk); |
169 | 169 | ||
170 | if (IS_ERR(syscon)) | 170 | if (IS_ERR(syscon)) |
171 | return ERR_CAST(syscon); | 171 | return ERR_CAST(syscon); |
172 | 172 | ||
173 | return syscon->regmap; | 173 | return syscon->regmap; |
174 | } | 174 | } |
175 | |||
176 | struct regmap *device_node_to_regmap(struct device_node *np) | ||
177 | { | ||
178 | return device_node_get_regmap(np, false); | ||
179 | } | ||
180 | EXPORT_SYMBOL_GPL(device_node_to_regmap); | ||
181 | |||
182 | struct regmap *syscon_node_to_regmap(struct device_node *np) | ||
183 | { | ||
184 | if (!of_device_is_compatible(np, "syscon")) | ||
185 | return ERR_PTR(-EINVAL); | ||
186 | |||
187 | return device_node_get_regmap(np, true); | ||
188 | } | ||
175 | EXPORT_SYMBOL_GPL(syscon_node_to_regmap); | 189 | EXPORT_SYMBOL_GPL(syscon_node_to_regmap); |
176 | 190 | ||
177 | struct regmap *syscon_regmap_lookup_by_compatible(const char *s) | 191 | struct regmap *syscon_regmap_lookup_by_compatible(const char *s) |
diff --git a/include/dt-bindings/clock/ingenic,tcu.h b/include/dt-bindings/clock/ingenic,tcu.h new file mode 100644 index 000000000000..d569650a7945 --- /dev/null +++ b/include/dt-bindings/clock/ingenic,tcu.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * This header provides clock numbers for the ingenic,tcu DT binding. | ||
4 | */ | ||
5 | |||
6 | #ifndef __DT_BINDINGS_CLOCK_INGENIC_TCU_H__ | ||
7 | #define __DT_BINDINGS_CLOCK_INGENIC_TCU_H__ | ||
8 | |||
9 | #define TCU_CLK_TIMER0 0 | ||
10 | #define TCU_CLK_TIMER1 1 | ||
11 | #define TCU_CLK_TIMER2 2 | ||
12 | #define TCU_CLK_TIMER3 3 | ||
13 | #define TCU_CLK_TIMER4 4 | ||
14 | #define TCU_CLK_TIMER5 5 | ||
15 | #define TCU_CLK_TIMER6 6 | ||
16 | #define TCU_CLK_TIMER7 7 | ||
17 | #define TCU_CLK_WDT 8 | ||
18 | #define TCU_CLK_OST 9 | ||
19 | |||
20 | #endif /* __DT_BINDINGS_CLOCK_INGENIC_TCU_H__ */ | ||
diff --git a/include/dt-bindings/clock/jz4740-cgu.h b/include/dt-bindings/clock/jz4740-cgu.h index 6ed83f926ae7..e82d77028581 100644 --- a/include/dt-bindings/clock/jz4740-cgu.h +++ b/include/dt-bindings/clock/jz4740-cgu.h | |||
@@ -34,5 +34,6 @@ | |||
34 | #define JZ4740_CLK_ADC 19 | 34 | #define JZ4740_CLK_ADC 19 |
35 | #define JZ4740_CLK_I2C 20 | 35 | #define JZ4740_CLK_I2C 20 |
36 | #define JZ4740_CLK_AIC 21 | 36 | #define JZ4740_CLK_AIC 21 |
37 | #define JZ4740_CLK_TCU 22 | ||
37 | 38 | ||
38 | #endif /* __DT_BINDINGS_CLOCK_JZ4740_CGU_H__ */ | 39 | #endif /* __DT_BINDINGS_CLOCK_JZ4740_CGU_H__ */ |
diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index 8cfda0554381..112dc66262cc 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h | |||
@@ -17,12 +17,18 @@ | |||
17 | struct device_node; | 17 | struct device_node; |
18 | 18 | ||
19 | #ifdef CONFIG_MFD_SYSCON | 19 | #ifdef CONFIG_MFD_SYSCON |
20 | extern struct regmap *device_node_to_regmap(struct device_node *np); | ||
20 | extern struct regmap *syscon_node_to_regmap(struct device_node *np); | 21 | extern struct regmap *syscon_node_to_regmap(struct device_node *np); |
21 | extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); | 22 | extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); |
22 | extern struct regmap *syscon_regmap_lookup_by_phandle( | 23 | extern struct regmap *syscon_regmap_lookup_by_phandle( |
23 | struct device_node *np, | 24 | struct device_node *np, |
24 | const char *property); | 25 | const char *property); |
25 | #else | 26 | #else |
27 | static inline struct regmap *device_node_to_regmap(struct device_node *np) | ||
28 | { | ||
29 | return ERR_PTR(-ENOTSUPP); | ||
30 | } | ||
31 | |||
26 | static inline struct regmap *syscon_node_to_regmap(struct device_node *np) | 32 | static inline struct regmap *syscon_node_to_regmap(struct device_node *np) |
27 | { | 33 | { |
28 | return ERR_PTR(-ENOTSUPP); | 34 | return ERR_PTR(-ENOTSUPP); |