···11-Ingenic JZ47xx PWM Controller22-=============================33-44-Required properties:55-- compatible: Should be "ingenic,jz4740-pwm"66-- #pwm-cells: Should be 3. See pwm.txt in this directory for a description77- of the cells format.88-- clocks : phandle to the external clock.99-- clock-names : Should be "ext".1010-1111-1212-Example:1313-1414- pwm: pwm@10002000 {1515- compatible = "ingenic,jz4740-pwm";1616- reg = <0x10002000 0x1000>;1717-1818- #pwm-cells = <3>;1919-2020- clocks = <&ext>;2121- clock-names = "ext";2222- };
···11+Ingenic JZ47xx SoCs Timer/Counter Unit devicetree bindings22+==========================================================33+44+For a description of the TCU hardware and drivers, have a look at55+Documentation/mips/ingenic-tcu.txt.66+77+Required properties:88+99+- compatible: Must be one of:1010+ * ingenic,jz4740-tcu1111+ * ingenic,jz4725b-tcu1212+ * ingenic,jz4770-tcu1313+ followed by "simple-mfd".1414+- reg: Should be the offset/length value corresponding to the TCU registers1515+- clocks: List of phandle & clock specifiers for clocks external to the TCU.1616+ The "pclk", "rtc" and "ext" clocks should be provided. The "tcu" clock1717+ should be provided if the SoC has it.1818+- clock-names: List of name strings for the external clocks.1919+- #clock-cells: Should be <1>;2020+ Clock consumers specify this argument to identify a clock. The valid values2121+ may be found in <dt-bindings/clock/ingenic,tcu.h>.2222+- interrupt-controller : Identifies the node as an interrupt controller2323+- #interrupt-cells : Specifies the number of cells needed to encode an2424+ interrupt source. The value should be 1.2525+- interrupts : Specifies the interrupt the controller is connected to.2626+2727+Optional properties:2828+2929+- ingenic,pwm-channels-mask: Bitmask of TCU channels reserved for PWM use.3030+ Default value is 0xfc.3131+3232+3333+Children nodes3434+==========================================================3535+3636+3737+PWM node:3838+---------3939+4040+Required properties:4141+4242+- compatible: Must be one of:4343+ * ingenic,jz4740-pwm4444+ * ingenic,jz4725b-pwm4545+- #pwm-cells: Should be 3. See ../pwm/pwm.txt for a description of the cell4646+ format.4747+- clocks: List of phandle & clock specifiers for the TCU clocks.4848+- clock-names: List of name strings for the TCU clocks.4949+5050+5151+Watchdog node:5252+--------------5353+5454+Required properties:5555+5656+- compatible: Must be "ingenic,jz4740-watchdog"5757+- clocks: phandle to the WDT clock5858+- clock-names: should be "wdt"5959+6060+6161+OS Timer node:6262+---------6363+6464+Required properties:6565+6666+- compatible: Must be one of:6767+ * ingenic,jz4725b-ost6868+ * ingenic,jz4770-ost6969+- clocks: phandle to the OST clock7070+- clock-names: should be "ost"7171+- interrupts : Specifies the interrupt the OST is connected to.7272+7373+7474+Example7575+==========================================================7676+7777+#include <dt-bindings/clock/jz4770-cgu.h>7878+#include <dt-bindings/clock/ingenic,tcu.h>7979+8080+/ {8181+ tcu: timer@10002000 {8282+ compatible = "ingenic,jz4770-tcu", "simple-mfd";8383+ reg = <0x10002000 0x1000>;8484+ #address-cells = <1>;8585+ #size-cells = <1>;8686+ ranges = <0x0 0x10002000 0x1000>;8787+8888+ #clock-cells = <1>;8989+9090+ clocks = <&cgu JZ4770_CLK_RTC9191+ &cgu JZ4770_CLK_EXT9292+ &cgu JZ4770_CLK_PCLK>;9393+ clock-names = "rtc", "ext", "pclk";9494+9595+ interrupt-controller;9696+ #interrupt-cells = <1>;9797+9898+ interrupt-parent = <&intc>;9999+ interrupts = <27 26 25>;100100+101101+ watchdog: watchdog@0 {102102+ compatible = "ingenic,jz4740-watchdog";103103+ reg = <0x0 0xc>;104104+105105+ clocks = <&tcu TCU_CLK_WDT>;106106+ clock-names = "wdt";107107+ };108108+109109+ pwm: pwm@40 {110110+ compatible = "ingenic,jz4740-pwm";111111+ reg = <0x40 0x80>;112112+113113+ #pwm-cells = <3>;114114+115115+ clocks = <&tcu TCU_CLK_TIMER0116116+ &tcu TCU_CLK_TIMER1117117+ &tcu TCU_CLK_TIMER2118118+ &tcu TCU_CLK_TIMER3119119+ &tcu TCU_CLK_TIMER4120120+ &tcu TCU_CLK_TIMER5121121+ &tcu TCU_CLK_TIMER6122122+ &tcu TCU_CLK_TIMER7>;123123+ clock-names = "timer0", "timer1", "timer2", "timer3",124124+ "timer4", "timer5", "timer6", "timer7";125125+ };126126+127127+ ost: timer@e0 {128128+ compatible = "ingenic,jz4770-ost";129129+ reg = <0xe0 0x20>;130130+131131+ clocks = <&tcu TCU_CLK_OST>;132132+ clock-names = "ost";133133+134134+ interrupts = <15>;135135+ };136136+ };137137+};
···11+.. SPDX-License-Identifier: GPL-2.022+33+===============================================44+Ingenic JZ47xx SoCs Timer/Counter Unit hardware55+===============================================66+77+The Timer/Counter Unit (TCU) in Ingenic JZ47xx SoCs is a multi-function88+hardware block. It features up to to eight channels, that can be used as99+counters, timers, or PWM.1010+1111+- JZ4725B, JZ4750, JZ4755 only have six TCU channels. The other SoCs all1212+ have eight channels.1313+1414+- JZ4725B introduced a separate channel, called Operating System Timer1515+ (OST). It is a 32-bit programmable timer. On JZ4760B and above, it is1616+ 64-bit.1717+1818+- Each one of the TCU channels has its own clock, which can be reparented to three1919+ different clocks (pclk, ext, rtc), gated, and reclocked, through their TCSR register.2020+2121+ - The watchdog and OST hardware blocks also feature a TCSR register with the same2222+ format in their register space.2323+ - The TCU registers used to gate/ungate can also gate/ungate the watchdog and2424+ OST clocks.2525+2626+- Each TCU channel works in one of two modes:2727+2828+ - mode TCU1: channels cannot work in sleep mode, but are easier to2929+ operate.3030+ - mode TCU2: channels can work in sleep mode, but the operation is a bit3131+ more complicated than with TCU1 channels.3232+3333+- The mode of each TCU channel depends on the SoC used:3434+3535+ - On the oldest SoCs (up to JZ4740), all of the eight channels operate in3636+ TCU1 mode.3737+ - On JZ4725B, channel 5 operates as TCU2, the others operate as TCU1.3838+ - On newest SoCs (JZ4750 and above), channels 1-2 operate as TCU2, the3939+ others operate as TCU1.4040+4141+- Each channel can generate an interrupt. Some channels share an interrupt4242+ line, some don't, and this changes between SoC versions:4343+4444+ - on older SoCs (JZ4740 and below), channel 0 and channel 1 have their4545+ own interrupt line; channels 2-7 share the last interrupt line.4646+ - On JZ4725B, channel 0 has its own interrupt; channels 1-5 share one4747+ interrupt line; the OST uses the last interrupt line.4848+ - on newer SoCs (JZ4750 and above), channel 5 has its own interrupt;4949+ channels 0-4 and (if eight channels) 6-7 all share one interrupt line;5050+ the OST uses the last interrupt line.5151+5252+Implementation5353+==============5454+5555+The functionalities of the TCU hardware are spread across multiple drivers:5656+5757+=========== =====5858+clocks drivers/clk/ingenic/tcu.c5959+interrupts drivers/irqchip/irq-ingenic-tcu.c6060+timers drivers/clocksource/ingenic-timer.c6161+OST drivers/clocksource/ingenic-ost.c6262+PWM drivers/pwm/pwm-jz4740.c6363+watchdog drivers/watchdog/jz4740_wdt.c6464+=========== =====6565+6666+Because various functionalities of the TCU that belong to different drivers6767+and frameworks can be controlled from the same registers, all of these6868+drivers access their registers through the same regmap.6969+7070+For more information regarding the devicetree bindings of the TCU drivers,7171+have a look at Documentation/devicetree/bindings/mfd/ingenic,tcu.txt.
+7
arch/mips/boot/dts/ingenic/ci20.dts
···22/dts-v1/;3344#include "jz4780.dtsi"55+#include <dt-bindings/clock/ingenic,tcu.h>56#include <dt-bindings/gpio/gpio.h>6778/ {···238237 groups = "mmc1-1bit-d", "mmc1-4bit-d";239238 bias-disable;240239 };240240+};241241+242242+&tcu {243243+ /* 3 MHz for the system timer and clocksource */244244+ assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>;245245+ assigned-clock-rates = <3000000>, <3000000>;241246};
+10
arch/mips/boot/dts/ingenic/gcw0.dts
···22/dts-v1/;3344#include "jz4770.dtsi"55+#include <dt-bindings/clock/ingenic,tcu.h>5667/ {78 compatible = "gcw,zero", "ingenic,jz4770";···6059&uhc {6160 /* The WiFi module is connected to the UHC. */6261 status = "okay";6262+};6363+6464+&tcu {6565+ /* 750 kHz for the system timer and clocksource */6666+ assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>;6767+ assigned-clock-rates = <750000>, <750000>;6868+6969+ /* PWM1 is in use, so reserve channel #2 for the clocksource */7070+ ingenic,pwm-channels-mask = <0xfa>;6371};
···11# SPDX-License-Identifier: GPL-2.0-only22-menu "Ingenic JZ47xx CGU drivers"22+menu "Ingenic SoCs drivers"33 depends on MIPS4455config INGENIC_CGU_COMMON···4444 and compatible SoCs.45454646 If building for a JZ4780 SoC, you want to say Y here.4747+4848+config INGENIC_TCU_CLK4949+ bool "Ingenic JZ47xx TCU clocks driver"5050+ default MACH_INGENIC5151+ select MFD_SYSCON5252+ help5353+ Support the clocks of the Timer/Counter Unit (TCU) of the Ingenic5454+ JZ47xx SoCs.47554856endmenu
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * JZ47xx SoCs TCU clocks driver44+ * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>55+ */66+77+#include <linux/clk.h>88+#include <linux/clk-provider.h>99+#include <linux/clockchips.h>1010+#include <linux/mfd/ingenic-tcu.h>1111+#include <linux/mfd/syscon.h>1212+#include <linux/regmap.h>1313+#include <linux/slab.h>1414+#include <linux/syscore_ops.h>1515+1616+#include <dt-bindings/clock/ingenic,tcu.h>1717+1818+/* 8 channels max + watchdog + OST */1919+#define TCU_CLK_COUNT 102020+2121+#undef pr_fmt2222+#define pr_fmt(fmt) "ingenic-tcu-clk: " fmt2323+2424+enum tcu_clk_parent {2525+ TCU_PARENT_PCLK,2626+ TCU_PARENT_RTC,2727+ TCU_PARENT_EXT,2828+};2929+3030+struct ingenic_soc_info {3131+ unsigned int num_channels;3232+ bool has_ost;3333+ bool has_tcu_clk;3434+};3535+3636+struct ingenic_tcu_clk_info {3737+ struct clk_init_data init_data;3838+ u8 gate_bit;3939+ u8 tcsr_reg;4040+};4141+4242+struct ingenic_tcu_clk {4343+ struct clk_hw hw;4444+ unsigned int idx;4545+ struct ingenic_tcu *tcu;4646+ const struct ingenic_tcu_clk_info *info;4747+};4848+4949+struct ingenic_tcu {5050+ const struct ingenic_soc_info *soc_info;5151+ struct regmap *map;5252+ struct clk *clk;5353+5454+ struct clk_hw_onecell_data *clocks;5555+};5656+5757+static struct ingenic_tcu *ingenic_tcu;5858+5959+static inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw)6060+{6161+ return container_of(hw, struct ingenic_tcu_clk, hw);6262+}6363+6464+static int ingenic_tcu_enable(struct clk_hw *hw)6565+{6666+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);6767+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;6868+ struct ingenic_tcu *tcu = tcu_clk->tcu;6969+7070+ regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));7171+7272+ return 0;7373+}7474+7575+static void ingenic_tcu_disable(struct clk_hw *hw)7676+{7777+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);7878+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;7979+ struct ingenic_tcu *tcu = tcu_clk->tcu;8080+8181+ regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));8282+}8383+8484+static int ingenic_tcu_is_enabled(struct clk_hw *hw)8585+{8686+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);8787+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;8888+ unsigned int value;8989+9090+ regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value);9191+9292+ return !(value & BIT(info->gate_bit));9393+}9494+9595+static bool ingenic_tcu_enable_regs(struct clk_hw *hw)9696+{9797+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);9898+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;9999+ struct ingenic_tcu *tcu = tcu_clk->tcu;100100+ bool enabled = false;101101+102102+ /*103103+ * If the SoC has no global TCU clock, we must ungate the channel's104104+ * clock to be able to access its registers.105105+ * If we have a TCU clock, it will be enabled automatically as it has106106+ * been attached to the regmap.107107+ */108108+ if (!tcu->clk) {109109+ enabled = !!ingenic_tcu_is_enabled(hw);110110+ regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));111111+ }112112+113113+ return enabled;114114+}115115+116116+static void ingenic_tcu_disable_regs(struct clk_hw *hw)117117+{118118+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);119119+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;120120+ struct ingenic_tcu *tcu = tcu_clk->tcu;121121+122122+ if (!tcu->clk)123123+ regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));124124+}125125+126126+static u8 ingenic_tcu_get_parent(struct clk_hw *hw)127127+{128128+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);129129+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;130130+ unsigned int val = 0;131131+ int ret;132132+133133+ ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val);134134+ WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);135135+136136+ return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1;137137+}138138+139139+static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx)140140+{141141+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);142142+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;143143+ bool was_enabled;144144+ int ret;145145+146146+ was_enabled = ingenic_tcu_enable_regs(hw);147147+148148+ ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,149149+ TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx));150150+ WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);151151+152152+ if (!was_enabled)153153+ ingenic_tcu_disable_regs(hw);154154+155155+ return 0;156156+}157157+158158+static unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw,159159+ unsigned long parent_rate)160160+{161161+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);162162+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;163163+ unsigned int prescale;164164+ int ret;165165+166166+ ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &prescale);167167+ WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);168168+169169+ prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB;170170+171171+ return parent_rate >> (prescale * 2);172172+}173173+174174+static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate)175175+{176176+ u8 prescale;177177+178178+ for (prescale = 0; prescale < 5; prescale++)179179+ if ((rate >> (prescale * 2)) <= req_rate)180180+ return prescale;181181+182182+ return 5; /* /1024 divider */183183+}184184+185185+static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,186186+ unsigned long *parent_rate)187187+{188188+ unsigned long rate = *parent_rate;189189+ u8 prescale;190190+191191+ if (req_rate > rate)192192+ return -EINVAL;193193+194194+ prescale = ingenic_tcu_get_prescale(rate, req_rate);195195+196196+ return rate >> (prescale * 2);197197+}198198+199199+static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,200200+ unsigned long parent_rate)201201+{202202+ struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);203203+ const struct ingenic_tcu_clk_info *info = tcu_clk->info;204204+ u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate);205205+ bool was_enabled;206206+ int ret;207207+208208+ was_enabled = ingenic_tcu_enable_regs(hw);209209+210210+ ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,211211+ TCU_TCSR_PRESCALE_MASK,212212+ prescale << TCU_TCSR_PRESCALE_LSB);213213+ WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);214214+215215+ if (!was_enabled)216216+ ingenic_tcu_disable_regs(hw);217217+218218+ return 0;219219+}220220+221221+static const struct clk_ops ingenic_tcu_clk_ops = {222222+ .get_parent = ingenic_tcu_get_parent,223223+ .set_parent = ingenic_tcu_set_parent,224224+225225+ .recalc_rate = ingenic_tcu_recalc_rate,226226+ .round_rate = ingenic_tcu_round_rate,227227+ .set_rate = ingenic_tcu_set_rate,228228+229229+ .enable = ingenic_tcu_enable,230230+ .disable = ingenic_tcu_disable,231231+ .is_enabled = ingenic_tcu_is_enabled,232232+};233233+234234+static const char * const ingenic_tcu_timer_parents[] = {235235+ [TCU_PARENT_PCLK] = "pclk",236236+ [TCU_PARENT_RTC] = "rtc",237237+ [TCU_PARENT_EXT] = "ext",238238+};239239+240240+#define DEF_TIMER(_name, _gate_bit, _tcsr) \241241+ { \242242+ .init_data = { \243243+ .name = _name, \244244+ .parent_names = ingenic_tcu_timer_parents, \245245+ .num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\246246+ .ops = &ingenic_tcu_clk_ops, \247247+ .flags = CLK_SET_RATE_UNGATE, \248248+ }, \249249+ .gate_bit = _gate_bit, \250250+ .tcsr_reg = _tcsr, \251251+ }252252+static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = {253253+ [TCU_CLK_TIMER0] = DEF_TIMER("timer0", 0, TCU_REG_TCSRc(0)),254254+ [TCU_CLK_TIMER1] = DEF_TIMER("timer1", 1, TCU_REG_TCSRc(1)),255255+ [TCU_CLK_TIMER2] = DEF_TIMER("timer2", 2, TCU_REG_TCSRc(2)),256256+ [TCU_CLK_TIMER3] = DEF_TIMER("timer3", 3, TCU_REG_TCSRc(3)),257257+ [TCU_CLK_TIMER4] = DEF_TIMER("timer4", 4, TCU_REG_TCSRc(4)),258258+ [TCU_CLK_TIMER5] = DEF_TIMER("timer5", 5, TCU_REG_TCSRc(5)),259259+ [TCU_CLK_TIMER6] = DEF_TIMER("timer6", 6, TCU_REG_TCSRc(6)),260260+ [TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)),261261+};262262+263263+static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info =264264+ DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR);265265+static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info =266266+ DEF_TIMER("ost", 15, TCU_REG_OST_TCSR);267267+#undef DEF_TIMER268268+269269+static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,270270+ unsigned int idx, enum tcu_clk_parent parent,271271+ const struct ingenic_tcu_clk_info *info,272272+ struct clk_hw_onecell_data *clocks)273273+{274274+ struct ingenic_tcu_clk *tcu_clk;275275+ int err;276276+277277+ tcu_clk = kzalloc(sizeof(*tcu_clk), GFP_KERNEL);278278+ if (!tcu_clk)279279+ return -ENOMEM;280280+281281+ tcu_clk->hw.init = &info->init_data;282282+ tcu_clk->idx = idx;283283+ tcu_clk->info = info;284284+ tcu_clk->tcu = tcu;285285+286286+ /* Reset channel and clock divider, set default parent */287287+ ingenic_tcu_enable_regs(&tcu_clk->hw);288288+ regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent));289289+ ingenic_tcu_disable_regs(&tcu_clk->hw);290290+291291+ err = clk_hw_register(NULL, &tcu_clk->hw);292292+ if (err) {293293+ kfree(tcu_clk);294294+ return err;295295+ }296296+297297+ clocks->hws[idx] = &tcu_clk->hw;298298+299299+ return 0;300300+}301301+302302+static const struct ingenic_soc_info jz4740_soc_info = {303303+ .num_channels = 8,304304+ .has_ost = false,305305+ .has_tcu_clk = true,306306+};307307+308308+static const struct ingenic_soc_info jz4725b_soc_info = {309309+ .num_channels = 6,310310+ .has_ost = true,311311+ .has_tcu_clk = true,312312+};313313+314314+static const struct ingenic_soc_info jz4770_soc_info = {315315+ .num_channels = 8,316316+ .has_ost = true,317317+ .has_tcu_clk = false,318318+};319319+320320+static const struct of_device_id ingenic_tcu_of_match[] __initconst = {321321+ { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },322322+ { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },323323+ { .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },324324+ { /* sentinel */ }325325+};326326+327327+static int __init ingenic_tcu_probe(struct device_node *np)328328+{329329+ const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np);330330+ struct ingenic_tcu *tcu;331331+ struct regmap *map;332332+ unsigned int i;333333+ int ret;334334+335335+ map = device_node_to_regmap(np);336336+ if (IS_ERR(map))337337+ return PTR_ERR(map);338338+339339+ tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);340340+ if (!tcu)341341+ return -ENOMEM;342342+343343+ tcu->map = map;344344+ tcu->soc_info = id->data;345345+346346+ if (tcu->soc_info->has_tcu_clk) {347347+ tcu->clk = of_clk_get_by_name(np, "tcu");348348+ if (IS_ERR(tcu->clk)) {349349+ ret = PTR_ERR(tcu->clk);350350+ pr_crit("Cannot get TCU clock\n");351351+ goto err_free_tcu;352352+ }353353+354354+ ret = clk_prepare_enable(tcu->clk);355355+ if (ret) {356356+ pr_crit("Unable to enable TCU clock\n");357357+ goto err_put_clk;358358+ }359359+ }360360+361361+ tcu->clocks = kzalloc(sizeof(*tcu->clocks) +362362+ sizeof(*tcu->clocks->hws) * TCU_CLK_COUNT,363363+ GFP_KERNEL);364364+ if (!tcu->clocks) {365365+ ret = -ENOMEM;366366+ goto err_clk_disable;367367+ }368368+369369+ tcu->clocks->num = TCU_CLK_COUNT;370370+371371+ for (i = 0; i < tcu->soc_info->num_channels; i++) {372372+ ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT,373373+ &ingenic_tcu_clk_info[i],374374+ tcu->clocks);375375+ if (ret) {376376+ pr_crit("cannot register clock %d\n", i);377377+ goto err_unregister_timer_clocks;378378+ }379379+ }380380+381381+ /*382382+ * We set EXT as the default parent clock for all the TCU clocks383383+ * except for the watchdog one, where we set the RTC clock as the384384+ * parent. Since the EXT and PCLK are much faster than the RTC clock,385385+ * the watchdog would kick after a maximum time of 5s, and we might386386+ * want a slower kicking time.387387+ */388388+ ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC,389389+ &ingenic_tcu_watchdog_clk_info,390390+ tcu->clocks);391391+ if (ret) {392392+ pr_crit("cannot register watchdog clock\n");393393+ goto err_unregister_timer_clocks;394394+ }395395+396396+ if (tcu->soc_info->has_ost) {397397+ ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST,398398+ TCU_PARENT_EXT,399399+ &ingenic_tcu_ost_clk_info,400400+ tcu->clocks);401401+ if (ret) {402402+ pr_crit("cannot register ost clock\n");403403+ goto err_unregister_watchdog_clock;404404+ }405405+ }406406+407407+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks);408408+ if (ret) {409409+ pr_crit("cannot add OF clock provider\n");410410+ goto err_unregister_ost_clock;411411+ }412412+413413+ ingenic_tcu = tcu;414414+415415+ return 0;416416+417417+err_unregister_ost_clock:418418+ if (tcu->soc_info->has_ost)419419+ clk_hw_unregister(tcu->clocks->hws[i + 1]);420420+err_unregister_watchdog_clock:421421+ clk_hw_unregister(tcu->clocks->hws[i]);422422+err_unregister_timer_clocks:423423+ for (i = 0; i < tcu->clocks->num; i++)424424+ if (tcu->clocks->hws[i])425425+ clk_hw_unregister(tcu->clocks->hws[i]);426426+ kfree(tcu->clocks);427427+err_clk_disable:428428+ if (tcu->soc_info->has_tcu_clk)429429+ clk_disable_unprepare(tcu->clk);430430+err_put_clk:431431+ if (tcu->soc_info->has_tcu_clk)432432+ clk_put(tcu->clk);433433+err_free_tcu:434434+ kfree(tcu);435435+ return ret;436436+}437437+438438+static int __maybe_unused tcu_pm_suspend(void)439439+{440440+ struct ingenic_tcu *tcu = ingenic_tcu;441441+442442+ if (tcu->clk)443443+ clk_disable(tcu->clk);444444+445445+ return 0;446446+}447447+448448+static void __maybe_unused tcu_pm_resume(void)449449+{450450+ struct ingenic_tcu *tcu = ingenic_tcu;451451+452452+ if (tcu->clk)453453+ clk_enable(tcu->clk);454454+}455455+456456+static struct syscore_ops __maybe_unused tcu_pm_ops = {457457+ .suspend = tcu_pm_suspend,458458+ .resume = tcu_pm_resume,459459+};460460+461461+static void __init ingenic_tcu_init(struct device_node *np)462462+{463463+ int ret = ingenic_tcu_probe(np);464464+465465+ if (ret)466466+ pr_crit("Failed to initialize TCU clocks: %d\n", ret);467467+468468+ if (IS_ENABLED(CONFIG_PM_SLEEP))469469+ register_syscore_ops(&tcu_pm_ops);470470+}471471+472472+CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);473473+CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);474474+CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);
+11
drivers/clocksource/Kconfig
···685685 help686686 Enables the support for Milbeaut timer driver.687687688688+config INGENIC_TIMER689689+ bool "Clocksource/timer using the TCU in Ingenic JZ SoCs"690690+ default MACH_INGENIC691691+ depends on MIPS || COMPILE_TEST692692+ depends on COMMON_CLK693693+ select MFD_SYSCON694694+ select TIMER_OF695695+ select IRQ_DOMAIN696696+ help697697+ Support for the timer/counter unit of the Ingenic JZ SoCs.698698+688699endmenu
···315315 depends on MACH_INGENIC316316 default y317317318318+config INGENIC_TCU_IRQ319319+ bool "Ingenic JZ47xx TCU interrupt controller"320320+ default MACH_INGENIC321321+ depends on MIPS || COMPILE_TEST322322+ select MFD_SYSCON323323+ help324324+ Support for interrupts in the Timer/Counter Unit (TCU) of the Ingenic325325+ JZ47xx SoCs.326326+327327+ If unsure, say N.328328+318329config RENESAS_H8300H_INTC319330 bool320331 select IRQ_DOMAIN