aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@mips.com>2019-08-08 18:33:16 -0400
committerPaul Burton <paul.burton@mips.com>2019-08-08 18:33:16 -0400
commit75b7329a4f08b3d2566afa25cfaddc8ddfb6dfa1 (patch)
tree17a4e177a2a245af450827330eef0f39f16be3cf
parent6393e60644862478a9da8c6599ffc04f7f94bedf (diff)
parentabc552284f6b1e8e6f153771dac1dff72e9d6d66 (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>
-rw-r--r--Documentation/devicetree/bindings/pwm/ingenic,jz47xx-pwm.txt22
-rw-r--r--Documentation/devicetree/bindings/timer/ingenic,tcu.txt137
-rw-r--r--Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt17
-rw-r--r--Documentation/index.rst1
-rw-r--r--Documentation/mips/index.rst11
-rw-r--r--Documentation/mips/ingenic-tcu.rst71
-rw-r--r--arch/mips/boot/dts/ingenic/ci20.dts7
-rw-r--r--arch/mips/boot/dts/ingenic/gcw0.dts10
-rw-r--r--arch/mips/boot/dts/ingenic/jz4740.dtsi22
-rw-r--r--arch/mips/boot/dts/ingenic/jz4770.dtsi21
-rw-r--r--arch/mips/boot/dts/ingenic/jz4780.dtsi23
-rw-r--r--arch/mips/boot/dts/ingenic/qi_lb60.dts8
-rw-r--r--arch/mips/jz4740/time.c151
-rw-r--r--drivers/clk/ingenic/Kconfig10
-rw-r--r--drivers/clk/ingenic/Makefile1
-rw-r--r--drivers/clk/ingenic/jz4740-cgu.c6
-rw-r--r--drivers/clk/ingenic/tcu.c474
-rw-r--r--drivers/clocksource/Kconfig11
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/ingenic-timer.c356
-rw-r--r--drivers/irqchip/Kconfig11
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-ingenic-tcu.c182
-rw-r--r--drivers/mfd/syscon.c46
-rw-r--r--include/dt-bindings/clock/ingenic,tcu.h20
-rw-r--r--include/dt-bindings/clock/jz4740-cgu.h1
-rw-r--r--include/linux/mfd/syscon.h6
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 @@
1Ingenic JZ47xx PWM Controller
2=============================
3
4Required 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
12Example:
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 @@
1Ingenic JZ47xx SoCs Timer/Counter Unit devicetree bindings
2==========================================================
3
4For a description of the TCU hardware and drivers, have a look at
5Documentation/mips/ingenic-tcu.txt.
6
7Required 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
27Optional properties:
28
29- ingenic,pwm-channels-mask: Bitmask of TCU channels reserved for PWM use.
30 Default value is 0xfc.
31
32
33Children nodes
34==========================================================
35
36
37PWM node:
38---------
39
40Required 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
51Watchdog node:
52--------------
53
54Required properties:
55
56- compatible: Must be "ingenic,jz4740-watchdog"
57- clocks: phandle to the WDT clock
58- clock-names: should be "wdt"
59
60
61OS Timer node:
62---------
63
64Required 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
74Example
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 @@
1Ingenic Watchdog Timer (WDT) Controller for JZ4740 & JZ4780
2
3Required properties:
4compatible: "ingenic,jz4740-watchdog" or "ingenic,jz4780-watchdog"
5reg: Register address and length for watchdog registers
6clocks: phandle to the RTC clock
7clock-names: should be "rtc"
8
9Example:
10
11watchdog: 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===========================
4MIPS-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===============================================
4Ingenic JZ47xx SoCs Timer/Counter Unit hardware
5===============================================
6
7The Timer/Counter Unit (TCU) in Ingenic JZ47xx SoCs is a multi-function
8hardware block. It features up to to eight channels, that can be used as
9counters, 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
52Implementation
53==============
54
55The functionalities of the TCU hardware are spread across multiple drivers:
56
57=========== =====
58clocks drivers/clk/ingenic/tcu.c
59interrupts drivers/irqchip/irq-ingenic-tcu.c
60timers drivers/clocksource/ingenic-timer.c
61OST drivers/clocksource/ingenic-ost.c
62PWM drivers/pwm/pwm-jz4740.c
63watchdog drivers/watchdog/jz4740_wdt.c
64=========== =====
65
66Because various functionalities of the TCU that belong to different drivers
67and frameworks can be controlled from the same registers, all of these
68drivers access their registers through the same regmap.
69
70For more information regarding the devicetree bindings of the TCU drivers,
71have 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
23static uint16_t jz4740_jiffies_per_tick;
24
25static u64 jz4740_clocksource_read(struct clocksource *cs)
26{
27 return jz4740_timer_get_count(TIMER_CLOCKSOURCE);
28}
29
30static 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
38static u64 notrace jz4740_read_sched_clock(void)
39{
40 return jz4740_timer_get_count(TIMER_CLOCKSOURCE);
41}
42
43static 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
57static 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
67static 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
75static int jz4740_clockevent_shutdown(struct clock_event_device *evt)
76{
77 jz4740_timer_disable(TIMER_CLOCKEVENT);
78
79 return 0;
80}
81
82static 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
92static 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
109static 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
116void __init plat_time_init(void) 12void __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
2menu "Ingenic JZ47xx CGU drivers" 2menu "Ingenic SoCs drivers"
3 depends on MIPS 3 depends on MIPS
4 4
5config INGENIC_CGU_COMMON 5config 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
48config 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
48endmenu 56endmenu
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
4obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o 4obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
5obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o 5obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
6obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o 6obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o
7obj-$(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
227static void __init jz4740_cgu_init(struct device_node *np) 233static 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
24enum tcu_clk_parent {
25 TCU_PARENT_PCLK,
26 TCU_PARENT_RTC,
27 TCU_PARENT_EXT,
28};
29
30struct ingenic_soc_info {
31 unsigned int num_channels;
32 bool has_ost;
33 bool has_tcu_clk;
34};
35
36struct ingenic_tcu_clk_info {
37 struct clk_init_data init_data;
38 u8 gate_bit;
39 u8 tcsr_reg;
40};
41
42struct 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
49struct 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
57static struct ingenic_tcu *ingenic_tcu;
58
59static 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
64static 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
75static 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
84static 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
95static 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
116static 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
126static 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
139static 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
158static 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
174static 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
185static 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
199static 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
221static 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
234static 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 }
252static 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
263static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info =
264 DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR);
265static 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
269static 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
302static const struct ingenic_soc_info jz4740_soc_info = {
303 .num_channels = 8,
304 .has_ost = false,
305 .has_tcu_clk = true,
306};
307
308static const struct ingenic_soc_info jz4725b_soc_info = {
309 .num_channels = 6,
310 .has_ost = true,
311 .has_tcu_clk = true,
312};
313
314static const struct ingenic_soc_info jz4770_soc_info = {
315 .num_channels = 8,
316 .has_ost = true,
317 .has_tcu_clk = false,
318};
319
320static 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
327static 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
417err_unregister_ost_clock:
418 if (tcu->soc_info->has_ost)
419 clk_hw_unregister(tcu->clocks->hws[i + 1]);
420err_unregister_watchdog_clock:
421 clk_hw_unregister(tcu->clocks->hws[i]);
422err_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);
427err_clk_disable:
428 if (tcu->soc_info->has_tcu_clk)
429 clk_disable_unprepare(tcu->clk);
430err_put_clk:
431 if (tcu->soc_info->has_tcu_clk)
432 clk_put(tcu->clk);
433err_free_tcu:
434 kfree(tcu);
435 return ret;
436}
437
438static 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
448static 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
456static struct syscore_ops __maybe_unused tcu_pm_ops = {
457 .suspend = tcu_pm_suspend,
458 .resume = tcu_pm_resume,
459};
460
461static 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
472CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
473CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);
474CLK_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
688config 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
688endmenu 699endmenu
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
80obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o 80obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
81obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o 81obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
82obj-$(CONFIG_H8300_TPU) += h8300_tpu.o 82obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
83obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o
83obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o 84obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
84obj-$(CONFIG_X86_NUMACHIP) += numachip.o 85obj-$(CONFIG_X86_NUMACHIP) += numachip.o
85obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o 86obj-$(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
24struct ingenic_soc_info {
25 unsigned int num_channels;
26};
27
28struct 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
38static struct ingenic_tcu *ingenic_tcu;
39
40static 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
50static u64 notrace ingenic_tcu_timer_cs_read(struct clocksource *cs)
51{
52 return ingenic_tcu_timer_read();
53}
54
55static inline struct ingenic_tcu *to_ingenic_tcu(struct clock_event_device *evt)
56{
57 return container_of(evt, struct ingenic_tcu, cevt);
58}
59
60static 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
69static 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
84static 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
97static 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
108static 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
160err_irq_dispose_mapping:
161 irq_dispose_mapping(timer_virq);
162err_clk_disable:
163 clk_disable_unprepare(tcu->timer_clk);
164err_clk_put:
165 clk_put(tcu->timer_clk);
166 return err;
167}
168
169static 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
214err_clk_disable:
215 clk_disable_unprepare(tcu->cs_clk);
216err_clk_put:
217 clk_put(tcu->cs_clk);
218 return err;
219}
220
221static const struct ingenic_soc_info jz4740_soc_info = {
222 .num_channels = 8,
223};
224
225static const struct ingenic_soc_info jz4725b_soc_info = {
226 .num_channels = 6,
227};
228
229static 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
236static 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
293err_tcu_clocksource_cleanup:
294 clocksource_unregister(&tcu->cs);
295 clk_disable_unprepare(tcu->cs_clk);
296 clk_put(tcu->cs_clk);
297err_free_ingenic_tcu:
298 kfree(tcu);
299 return ret;
300}
301
302TIMER_OF_DECLARE(jz4740_tcu_intc, "ingenic,jz4740-tcu", ingenic_tcu_init);
303TIMER_OF_DECLARE(jz4725b_tcu_intc, "ingenic,jz4725b-tcu", ingenic_tcu_init);
304TIMER_OF_DECLARE(jz4770_tcu_intc, "ingenic,jz4770-tcu", ingenic_tcu_init);
305
306
307static int __init ingenic_tcu_probe(struct platform_device *pdev)
308{
309 platform_set_drvdata(pdev, ingenic_tcu);
310
311 return 0;
312}
313
314static 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
323static 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
341static 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
347static 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};
356builtin_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
318config 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
318config RENESAS_H8300H_INTC 329config 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
75obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o 75obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
76obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o 76obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
77obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o 77obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
78obj-$(CONFIG_INGENIC_TCU_IRQ) += irq-ingenic-tcu.o
78obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o 79obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
79obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o 80obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
80obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o 81obj-$(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
16struct 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
24static 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
46static 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
60static 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
73static 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
86static 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
171out_unmap_irqs:
172 for (; i > 0; i--)
173 irq_dispose_mapping(tcu->parent_irqs[i - 1]);
174out_domain_remove:
175 irq_domain_remove(tcu->domain);
176err_free_tcu:
177 kfree(tcu);
178 return ret;
179}
180IRQCHIP_DECLARE(jz4740_tcu_irq, "ingenic,jz4740-tcu", ingenic_tcu_irq_init);
181IRQCHIP_DECLARE(jz4725b_tcu_irq, "ingenic,jz4725b-tcu", ingenic_tcu_irq_init);
182IRQCHIP_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
43static struct syscon *of_syscon_register(struct device_node *np) 43static 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
153struct regmap *syscon_node_to_regmap(struct device_node *np) 152static 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
176struct regmap *device_node_to_regmap(struct device_node *np)
177{
178 return device_node_get_regmap(np, false);
179}
180EXPORT_SYMBOL_GPL(device_node_to_regmap);
181
182struct 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}
175EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 189EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
176 190
177struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 191struct 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 @@
17struct device_node; 17struct device_node;
18 18
19#ifdef CONFIG_MFD_SYSCON 19#ifdef CONFIG_MFD_SYSCON
20extern struct regmap *device_node_to_regmap(struct device_node *np);
20extern struct regmap *syscon_node_to_regmap(struct device_node *np); 21extern struct regmap *syscon_node_to_regmap(struct device_node *np);
21extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); 22extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s);
22extern struct regmap *syscon_regmap_lookup_by_phandle( 23extern 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
27static inline struct regmap *device_node_to_regmap(struct device_node *np)
28{
29 return ERR_PTR(-ENOTSUPP);
30}
31
26static inline struct regmap *syscon_node_to_regmap(struct device_node *np) 32static inline struct regmap *syscon_node_to_regmap(struct device_node *np)
27{ 33{
28 return ERR_PTR(-ENOTSUPP); 34 return ERR_PTR(-ENOTSUPP);