Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+1421 -206
-22
Documentation/devicetree/bindings/pwm/ingenic,jz47xx-pwm.txt
··· 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 - };
+137
Documentation/devicetree/bindings/timer/ingenic,tcu.txt
··· 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 + };
-17
Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt
··· 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 - };
+1
Documentation/index.rst
··· 143 143 arm64/index 144 144 ia64/index 145 145 m68k/index 146 + mips/index 146 147 riscv/index 147 148 s390/index 148 149 sh/index
+11
Documentation/mips/index.rst
··· 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
+71
Documentation/mips/ingenic-tcu.rst
··· 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.
+7
arch/mips/boot/dts/ingenic/ci20.dts
··· 2 2 /dts-v1/; 3 3 4 4 #include "jz4780.dtsi" 5 + #include <dt-bindings/clock/ingenic,tcu.h> 5 6 #include <dt-bindings/gpio/gpio.h> 6 7 7 8 / { ··· 238 237 groups = "mmc1-1bit-d", "mmc1-4bit-d"; 239 238 bias-disable; 240 239 }; 240 + }; 241 + 242 + &tcu { 243 + /* 3 MHz for the system timer and clocksource */ 244 + assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>; 245 + assigned-clock-rates = <3000000>, <3000000>; 241 246 };
+10
arch/mips/boot/dts/ingenic/gcw0.dts
··· 2 2 /dts-v1/; 3 3 4 4 #include "jz4770.dtsi" 5 + #include <dt-bindings/clock/ingenic,tcu.h> 5 6 6 7 / { 7 8 compatible = "gcw,zero", "ingenic,jz4770"; ··· 60 59 &uhc { 61 60 /* The WiFi module is connected to the UHC. */ 62 61 status = "okay"; 62 + }; 63 + 64 + &tcu { 65 + /* 750 kHz for the system timer and clocksource */ 66 + assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>; 67 + assigned-clock-rates = <750000>, <750000>; 68 + 69 + /* PWM1 is in use, so reserve channel #2 for the clocksource */ 70 + ingenic,pwm-channels-mask = <0xfa>; 63 71 };
+22
arch/mips/boot/dts/ingenic/jz4740.dtsi
··· 53 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 78 rtc_dev: rtc@10003000 { 57 79 compatible = "ingenic,jz4740-rtc"; 58 80 reg = <0x10003000 0x40>;
+21
arch/mips/boot/dts/ingenic/jz4770.dtsi
··· 46 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 70 pinctrl: pin-controller@10010000 { 50 71 compatible = "ingenic,jz4770-pinctrl"; 51 72 reg = <0x10010000 0x600>;
+23
arch/mips/boot/dts/ingenic/jz4780.dtsi
··· 46 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 72 rtc_dev: rtc@10003000 { 50 73 compatible = "ingenic,jz4780-rtc"; 51 74 reg = <0x10003000 0x4c>;
+7 -1
arch/mips/boot/dts/ingenic/qi_lb60.dts
··· 2 2 /dts-v1/; 3 3 4 4 #include "jz4740.dtsi" 5 - #include <dt-bindings/gpio/gpio.h> 6 5 7 6 #include <dt-bindings/gpio/gpio.h> 8 7 #include <dt-bindings/iio/adc/ingenic,adc.h> 8 + #include <dt-bindings/clock/ingenic,tcu.h> 9 9 #include <dt-bindings/input/input.h> 10 10 11 11 #define KEY_QI_QI KEY_F13 ··· 349 349 350 350 pinctrl-names = "default"; 351 351 pinctrl-0 = <&pins_mmc>; 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>; 352 358 };
+2 -149
arch/mips/jz4740/time.c
··· 4 4 * JZ4740 platform time support 5 5 */ 6 6 7 - #include <linux/clk.h> 8 7 #include <linux/clk-provider.h> 9 - #include <linux/interrupt.h> 10 - #include <linux/kernel.h> 11 - #include <linux/time.h> 8 + #include <linux/clocksource.h> 12 9 13 - #include <linux/clockchips.h> 14 - #include <linux/sched_clock.h> 15 - 16 - #include <asm/mach-jz4740/irq.h> 17 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 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 14 of_clk_init(NULL); 124 15 jz4740_timer_init(); 125 - 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); 16 + timer_probe(); 164 17 }
+9 -1
drivers/clk/ingenic/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - menu "Ingenic JZ47xx CGU drivers" 2 + menu "Ingenic SoCs drivers" 3 3 depends on MIPS 4 4 5 5 config INGENIC_CGU_COMMON ··· 44 44 and compatible SoCs. 45 45 46 46 If building for a JZ4780 SoC, you want to say Y here. 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. 47 55 48 56 endmenu
+1
drivers/clk/ingenic/Makefile
··· 4 4 obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o 5 5 obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o 6 6 obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o 7 + obj-$(CONFIG_INGENIC_TCU_CLK) += tcu.o
+6
drivers/clk/ingenic/jz4740-cgu.c
··· 222 222 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 223 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 233 static void __init jz4740_cgu_init(struct device_node *np)
+474
drivers/clk/ingenic/tcu.c
··· 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);
+11
drivers/clocksource/Kconfig
··· 685 685 help 686 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 699 endmenu
+1
drivers/clocksource/Makefile
··· 80 80 obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o 81 81 obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o 82 82 obj-$(CONFIG_H8300_TPU) += h8300_tpu.o 83 + obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o 83 84 obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o 84 85 obj-$(CONFIG_X86_NUMACHIP) += numachip.o 85 86 obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
+356
drivers/clocksource/ingenic-timer.c
··· 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);
+11
drivers/irqchip/Kconfig
··· 315 315 depends on MACH_INGENIC 316 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 329 config RENESAS_H8300H_INTC 319 330 bool 320 331 select IRQ_DOMAIN
+1
drivers/irqchip/Makefile
··· 75 75 obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o 76 76 obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o 77 77 obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o 78 + obj-$(CONFIG_INGENIC_TCU_IRQ) += irq-ingenic-tcu.o 78 79 obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o 79 80 obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o 80 81 obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o
+182
drivers/irqchip/irq-ingenic-tcu.c
··· 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);
+30 -16
drivers/mfd/syscon.c
··· 40 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 45 struct clk *clk; 46 46 struct syscon *syscon; ··· 50 50 int ret; 51 51 struct regmap_config syscon_config = syscon_regmap_config; 52 52 struct resource res; 53 - 54 - if (!of_device_is_compatible(np, "syscon")) 55 - return ERR_PTR(-EINVAL); 56 53 57 54 syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); 58 55 if (!syscon) ··· 114 117 goto err_regmap; 115 118 } 116 119 117 - clk = of_clk_get(np, 0); 118 - if (IS_ERR(clk)) { 119 - ret = PTR_ERR(clk); 120 - /* clock is optional */ 121 - if (ret != -ENOENT) 122 - goto err_clk; 123 - } else { 124 - ret = regmap_mmio_attach_clk(regmap, clk); 125 - if (ret) 126 - goto err_attach; 120 + if (check_clk) { 121 + clk = of_clk_get(np, 0); 122 + if (IS_ERR(clk)) { 123 + ret = PTR_ERR(clk); 124 + /* clock is optional */ 125 + if (ret != -ENOENT) 126 + goto err_clk; 127 + } else { 128 + ret = regmap_mmio_attach_clk(regmap, clk); 129 + if (ret) 130 + goto err_attach; 131 + } 127 132 } 128 133 129 134 syscon->regmap = regmap; ··· 149 150 return ERR_PTR(ret); 150 151 } 151 152 152 - struct regmap *syscon_node_to_regmap(struct device_node *np) 153 + static struct regmap *device_node_get_regmap(struct device_node *np, 154 + bool check_clk) 153 155 { 154 156 struct syscon *entry, *syscon = NULL; 155 157 ··· 165 165 spin_unlock(&syscon_list_slock); 166 166 167 167 if (!syscon) 168 - syscon = of_syscon_register(np); 168 + syscon = of_syscon_register(np, check_clk); 169 169 170 170 if (IS_ERR(syscon)) 171 171 return ERR_CAST(syscon); 172 172 173 173 return syscon->regmap; 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); 174 188 } 175 189 EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 176 190
+20
include/dt-bindings/clock/ingenic,tcu.h
··· 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__ */
+1
include/dt-bindings/clock/jz4740-cgu.h
··· 34 34 #define JZ4740_CLK_ADC 19 35 35 #define JZ4740_CLK_I2C 20 36 36 #define JZ4740_CLK_AIC 21 37 + #define JZ4740_CLK_TCU 22 37 38 38 39 #endif /* __DT_BINDINGS_CLOCK_JZ4740_CGU_H__ */
+6
include/linux/mfd/syscon.h
··· 17 17 struct device_node; 18 18 19 19 #ifdef CONFIG_MFD_SYSCON 20 + extern struct regmap *device_node_to_regmap(struct device_node *np); 20 21 extern struct regmap *syscon_node_to_regmap(struct device_node *np); 21 22 extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); 22 23 extern struct regmap *syscon_regmap_lookup_by_phandle( 23 24 struct device_node *np, 24 25 const char *property); 25 26 #else 27 + static inline struct regmap *device_node_to_regmap(struct device_node *np) 28 + { 29 + return ERR_PTR(-ENOTSUPP); 30 + } 31 + 26 32 static inline struct regmap *syscon_node_to_regmap(struct device_node *np) 27 33 { 28 34 return ERR_PTR(-ENOTSUPP);