14f89e4b8SPaul Cercueil // SPDX-License-Identifier: GPL-2.0
24f89e4b8SPaul Cercueil /*
34f89e4b8SPaul Cercueil * JZ47xx SoCs TCU clocks driver
44f89e4b8SPaul Cercueil * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
54f89e4b8SPaul Cercueil */
64f89e4b8SPaul Cercueil
74f89e4b8SPaul Cercueil #include <linux/clk.h>
84f89e4b8SPaul Cercueil #include <linux/clk-provider.h>
94f89e4b8SPaul Cercueil #include <linux/clockchips.h>
104f89e4b8SPaul Cercueil #include <linux/mfd/ingenic-tcu.h>
114f89e4b8SPaul Cercueil #include <linux/mfd/syscon.h>
124f89e4b8SPaul Cercueil #include <linux/regmap.h>
134f89e4b8SPaul Cercueil #include <linux/slab.h>
144f89e4b8SPaul Cercueil #include <linux/syscore_ops.h>
154f89e4b8SPaul Cercueil
164f89e4b8SPaul Cercueil #include <dt-bindings/clock/ingenic,tcu.h>
174f89e4b8SPaul Cercueil
184f89e4b8SPaul Cercueil /* 8 channels max + watchdog + OST */
194f89e4b8SPaul Cercueil #define TCU_CLK_COUNT 10
204f89e4b8SPaul Cercueil
214f89e4b8SPaul Cercueil #undef pr_fmt
224f89e4b8SPaul Cercueil #define pr_fmt(fmt) "ingenic-tcu-clk: " fmt
234f89e4b8SPaul Cercueil
244f89e4b8SPaul Cercueil enum tcu_clk_parent {
254f89e4b8SPaul Cercueil TCU_PARENT_PCLK,
264f89e4b8SPaul Cercueil TCU_PARENT_RTC,
274f89e4b8SPaul Cercueil TCU_PARENT_EXT,
284f89e4b8SPaul Cercueil };
294f89e4b8SPaul Cercueil
304f89e4b8SPaul Cercueil struct ingenic_soc_info {
314f89e4b8SPaul Cercueil unsigned int num_channels;
324f89e4b8SPaul Cercueil bool has_ost;
334f89e4b8SPaul Cercueil bool has_tcu_clk;
34e98839fbSAidan MacDonald bool allow_missing_tcu_clk;
354f89e4b8SPaul Cercueil };
364f89e4b8SPaul Cercueil
374f89e4b8SPaul Cercueil struct ingenic_tcu_clk_info {
384f89e4b8SPaul Cercueil struct clk_init_data init_data;
394f89e4b8SPaul Cercueil u8 gate_bit;
404f89e4b8SPaul Cercueil u8 tcsr_reg;
414f89e4b8SPaul Cercueil };
424f89e4b8SPaul Cercueil
434f89e4b8SPaul Cercueil struct ingenic_tcu_clk {
444f89e4b8SPaul Cercueil struct clk_hw hw;
454f89e4b8SPaul Cercueil unsigned int idx;
464f89e4b8SPaul Cercueil struct ingenic_tcu *tcu;
474f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info;
484f89e4b8SPaul Cercueil };
494f89e4b8SPaul Cercueil
504f89e4b8SPaul Cercueil struct ingenic_tcu {
514f89e4b8SPaul Cercueil const struct ingenic_soc_info *soc_info;
524f89e4b8SPaul Cercueil struct regmap *map;
534f89e4b8SPaul Cercueil struct clk *clk;
544f89e4b8SPaul Cercueil
554f89e4b8SPaul Cercueil struct clk_hw_onecell_data *clocks;
564f89e4b8SPaul Cercueil };
574f89e4b8SPaul Cercueil
584f89e4b8SPaul Cercueil static struct ingenic_tcu *ingenic_tcu;
594f89e4b8SPaul Cercueil
to_tcu_clk(struct clk_hw * hw)604f89e4b8SPaul Cercueil static inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw)
614f89e4b8SPaul Cercueil {
624f89e4b8SPaul Cercueil return container_of(hw, struct ingenic_tcu_clk, hw);
634f89e4b8SPaul Cercueil }
644f89e4b8SPaul Cercueil
ingenic_tcu_enable(struct clk_hw * hw)654f89e4b8SPaul Cercueil static int ingenic_tcu_enable(struct clk_hw *hw)
664f89e4b8SPaul Cercueil {
674f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
684f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
694f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = tcu_clk->tcu;
704f89e4b8SPaul Cercueil
714f89e4b8SPaul Cercueil regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
724f89e4b8SPaul Cercueil
734f89e4b8SPaul Cercueil return 0;
744f89e4b8SPaul Cercueil }
754f89e4b8SPaul Cercueil
ingenic_tcu_disable(struct clk_hw * hw)764f89e4b8SPaul Cercueil static void ingenic_tcu_disable(struct clk_hw *hw)
774f89e4b8SPaul Cercueil {
784f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
794f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
804f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = tcu_clk->tcu;
814f89e4b8SPaul Cercueil
824f89e4b8SPaul Cercueil regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
834f89e4b8SPaul Cercueil }
844f89e4b8SPaul Cercueil
ingenic_tcu_is_enabled(struct clk_hw * hw)854f89e4b8SPaul Cercueil static int ingenic_tcu_is_enabled(struct clk_hw *hw)
864f89e4b8SPaul Cercueil {
874f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
884f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
894f89e4b8SPaul Cercueil unsigned int value;
904f89e4b8SPaul Cercueil
914f89e4b8SPaul Cercueil regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value);
924f89e4b8SPaul Cercueil
934f89e4b8SPaul Cercueil return !(value & BIT(info->gate_bit));
944f89e4b8SPaul Cercueil }
954f89e4b8SPaul Cercueil
ingenic_tcu_enable_regs(struct clk_hw * hw)964f89e4b8SPaul Cercueil static bool ingenic_tcu_enable_regs(struct clk_hw *hw)
974f89e4b8SPaul Cercueil {
984f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
994f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
1004f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = tcu_clk->tcu;
1014f89e4b8SPaul Cercueil bool enabled = false;
1024f89e4b8SPaul Cercueil
1034f89e4b8SPaul Cercueil /*
1046726d552SAidan MacDonald * According to the programming manual, a timer channel's registers can
1056726d552SAidan MacDonald * only be accessed when the channel's stop bit is clear.
1064f89e4b8SPaul Cercueil */
1074f89e4b8SPaul Cercueil enabled = !!ingenic_tcu_is_enabled(hw);
1084f89e4b8SPaul Cercueil regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
1094f89e4b8SPaul Cercueil
1104f89e4b8SPaul Cercueil return enabled;
1114f89e4b8SPaul Cercueil }
1124f89e4b8SPaul Cercueil
ingenic_tcu_disable_regs(struct clk_hw * hw)1134f89e4b8SPaul Cercueil static void ingenic_tcu_disable_regs(struct clk_hw *hw)
1144f89e4b8SPaul Cercueil {
1154f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
1164f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
1174f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = tcu_clk->tcu;
1184f89e4b8SPaul Cercueil
1194f89e4b8SPaul Cercueil regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
1204f89e4b8SPaul Cercueil }
1214f89e4b8SPaul Cercueil
ingenic_tcu_get_parent(struct clk_hw * hw)1224f89e4b8SPaul Cercueil static u8 ingenic_tcu_get_parent(struct clk_hw *hw)
1234f89e4b8SPaul Cercueil {
1244f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
1254f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
1264f89e4b8SPaul Cercueil unsigned int val = 0;
1274f89e4b8SPaul Cercueil int ret;
1284f89e4b8SPaul Cercueil
1294f89e4b8SPaul Cercueil ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val);
1304f89e4b8SPaul Cercueil WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);
1314f89e4b8SPaul Cercueil
1324f89e4b8SPaul Cercueil return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1;
1334f89e4b8SPaul Cercueil }
1344f89e4b8SPaul Cercueil
ingenic_tcu_set_parent(struct clk_hw * hw,u8 idx)1354f89e4b8SPaul Cercueil static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx)
1364f89e4b8SPaul Cercueil {
1374f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
1384f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
1394f89e4b8SPaul Cercueil bool was_enabled;
1404f89e4b8SPaul Cercueil int ret;
1414f89e4b8SPaul Cercueil
1424f89e4b8SPaul Cercueil was_enabled = ingenic_tcu_enable_regs(hw);
1434f89e4b8SPaul Cercueil
1444f89e4b8SPaul Cercueil ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
1454f89e4b8SPaul Cercueil TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx));
1464f89e4b8SPaul Cercueil WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);
1474f89e4b8SPaul Cercueil
1484f89e4b8SPaul Cercueil if (!was_enabled)
1494f89e4b8SPaul Cercueil ingenic_tcu_disable_regs(hw);
1504f89e4b8SPaul Cercueil
1514f89e4b8SPaul Cercueil return 0;
1524f89e4b8SPaul Cercueil }
1534f89e4b8SPaul Cercueil
ingenic_tcu_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)1544f89e4b8SPaul Cercueil static unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw,
1554f89e4b8SPaul Cercueil unsigned long parent_rate)
1564f89e4b8SPaul Cercueil {
1574f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
1584f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
1594f89e4b8SPaul Cercueil unsigned int prescale;
1604f89e4b8SPaul Cercueil int ret;
1614f89e4b8SPaul Cercueil
1624f89e4b8SPaul Cercueil ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &prescale);
1634f89e4b8SPaul Cercueil WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);
1644f89e4b8SPaul Cercueil
1654f89e4b8SPaul Cercueil prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB;
1664f89e4b8SPaul Cercueil
1674f89e4b8SPaul Cercueil return parent_rate >> (prescale * 2);
1684f89e4b8SPaul Cercueil }
1694f89e4b8SPaul Cercueil
ingenic_tcu_get_prescale(unsigned long rate,unsigned long req_rate)1704f89e4b8SPaul Cercueil static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate)
1714f89e4b8SPaul Cercueil {
1724f89e4b8SPaul Cercueil u8 prescale;
1734f89e4b8SPaul Cercueil
1744f89e4b8SPaul Cercueil for (prescale = 0; prescale < 5; prescale++)
1754f89e4b8SPaul Cercueil if ((rate >> (prescale * 2)) <= req_rate)
1764f89e4b8SPaul Cercueil return prescale;
1774f89e4b8SPaul Cercueil
1784f89e4b8SPaul Cercueil return 5; /* /1024 divider */
1794f89e4b8SPaul Cercueil }
1804f89e4b8SPaul Cercueil
ingenic_tcu_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)181*65c1f037SMaxime Ripard static int ingenic_tcu_determine_rate(struct clk_hw *hw,
182*65c1f037SMaxime Ripard struct clk_rate_request *req)
1834f89e4b8SPaul Cercueil {
184*65c1f037SMaxime Ripard unsigned long rate = req->best_parent_rate;
1854f89e4b8SPaul Cercueil u8 prescale;
1864f89e4b8SPaul Cercueil
187*65c1f037SMaxime Ripard if (req->rate > rate) {
188*65c1f037SMaxime Ripard req->rate = rate;
189*65c1f037SMaxime Ripard return 0;
190*65c1f037SMaxime Ripard }
1914f89e4b8SPaul Cercueil
192*65c1f037SMaxime Ripard prescale = ingenic_tcu_get_prescale(rate, req->rate);
1934f89e4b8SPaul Cercueil
194*65c1f037SMaxime Ripard req->rate = rate >> (prescale * 2);
195*65c1f037SMaxime Ripard return 0;
1964f89e4b8SPaul Cercueil }
1974f89e4b8SPaul Cercueil
ingenic_tcu_set_rate(struct clk_hw * hw,unsigned long req_rate,unsigned long parent_rate)1984f89e4b8SPaul Cercueil static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,
1994f89e4b8SPaul Cercueil unsigned long parent_rate)
2004f89e4b8SPaul Cercueil {
2014f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
2024f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info = tcu_clk->info;
2034f89e4b8SPaul Cercueil u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate);
2044f89e4b8SPaul Cercueil bool was_enabled;
2054f89e4b8SPaul Cercueil int ret;
2064f89e4b8SPaul Cercueil
2074f89e4b8SPaul Cercueil was_enabled = ingenic_tcu_enable_regs(hw);
2084f89e4b8SPaul Cercueil
2094f89e4b8SPaul Cercueil ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
2104f89e4b8SPaul Cercueil TCU_TCSR_PRESCALE_MASK,
2114f89e4b8SPaul Cercueil prescale << TCU_TCSR_PRESCALE_LSB);
2124f89e4b8SPaul Cercueil WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);
2134f89e4b8SPaul Cercueil
2144f89e4b8SPaul Cercueil if (!was_enabled)
2154f89e4b8SPaul Cercueil ingenic_tcu_disable_regs(hw);
2164f89e4b8SPaul Cercueil
2174f89e4b8SPaul Cercueil return 0;
2184f89e4b8SPaul Cercueil }
2194f89e4b8SPaul Cercueil
2204f89e4b8SPaul Cercueil static const struct clk_ops ingenic_tcu_clk_ops = {
2214f89e4b8SPaul Cercueil .get_parent = ingenic_tcu_get_parent,
2224f89e4b8SPaul Cercueil .set_parent = ingenic_tcu_set_parent,
2234f89e4b8SPaul Cercueil
2244f89e4b8SPaul Cercueil .recalc_rate = ingenic_tcu_recalc_rate,
225*65c1f037SMaxime Ripard .determine_rate = ingenic_tcu_determine_rate,
2264f89e4b8SPaul Cercueil .set_rate = ingenic_tcu_set_rate,
2274f89e4b8SPaul Cercueil
2284f89e4b8SPaul Cercueil .enable = ingenic_tcu_enable,
2294f89e4b8SPaul Cercueil .disable = ingenic_tcu_disable,
2304f89e4b8SPaul Cercueil .is_enabled = ingenic_tcu_is_enabled,
2314f89e4b8SPaul Cercueil };
2324f89e4b8SPaul Cercueil
2334f89e4b8SPaul Cercueil static const char * const ingenic_tcu_timer_parents[] = {
2344f89e4b8SPaul Cercueil [TCU_PARENT_PCLK] = "pclk",
2354f89e4b8SPaul Cercueil [TCU_PARENT_RTC] = "rtc",
2364f89e4b8SPaul Cercueil [TCU_PARENT_EXT] = "ext",
2374f89e4b8SPaul Cercueil };
2384f89e4b8SPaul Cercueil
2394f89e4b8SPaul Cercueil #define DEF_TIMER(_name, _gate_bit, _tcsr) \
2404f89e4b8SPaul Cercueil { \
2414f89e4b8SPaul Cercueil .init_data = { \
2424f89e4b8SPaul Cercueil .name = _name, \
2434f89e4b8SPaul Cercueil .parent_names = ingenic_tcu_timer_parents, \
2444f89e4b8SPaul Cercueil .num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\
2454f89e4b8SPaul Cercueil .ops = &ingenic_tcu_clk_ops, \
2464f89e4b8SPaul Cercueil .flags = CLK_SET_RATE_UNGATE, \
2474f89e4b8SPaul Cercueil }, \
2484f89e4b8SPaul Cercueil .gate_bit = _gate_bit, \
2494f89e4b8SPaul Cercueil .tcsr_reg = _tcsr, \
2504f89e4b8SPaul Cercueil }
2514f89e4b8SPaul Cercueil static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = {
2524f89e4b8SPaul Cercueil [TCU_CLK_TIMER0] = DEF_TIMER("timer0", 0, TCU_REG_TCSRc(0)),
2534f89e4b8SPaul Cercueil [TCU_CLK_TIMER1] = DEF_TIMER("timer1", 1, TCU_REG_TCSRc(1)),
2544f89e4b8SPaul Cercueil [TCU_CLK_TIMER2] = DEF_TIMER("timer2", 2, TCU_REG_TCSRc(2)),
2554f89e4b8SPaul Cercueil [TCU_CLK_TIMER3] = DEF_TIMER("timer3", 3, TCU_REG_TCSRc(3)),
2564f89e4b8SPaul Cercueil [TCU_CLK_TIMER4] = DEF_TIMER("timer4", 4, TCU_REG_TCSRc(4)),
2574f89e4b8SPaul Cercueil [TCU_CLK_TIMER5] = DEF_TIMER("timer5", 5, TCU_REG_TCSRc(5)),
2584f89e4b8SPaul Cercueil [TCU_CLK_TIMER6] = DEF_TIMER("timer6", 6, TCU_REG_TCSRc(6)),
2594f89e4b8SPaul Cercueil [TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)),
2604f89e4b8SPaul Cercueil };
2614f89e4b8SPaul Cercueil
2624f89e4b8SPaul Cercueil static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info =
2634f89e4b8SPaul Cercueil DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR);
2644f89e4b8SPaul Cercueil static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info =
2654f89e4b8SPaul Cercueil DEF_TIMER("ost", 15, TCU_REG_OST_TCSR);
2664f89e4b8SPaul Cercueil #undef DEF_TIMER
2674f89e4b8SPaul Cercueil
ingenic_tcu_register_clock(struct ingenic_tcu * tcu,unsigned int idx,enum tcu_clk_parent parent,const struct ingenic_tcu_clk_info * info,struct clk_hw_onecell_data * clocks)2684f89e4b8SPaul Cercueil static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,
2694f89e4b8SPaul Cercueil unsigned int idx, enum tcu_clk_parent parent,
2704f89e4b8SPaul Cercueil const struct ingenic_tcu_clk_info *info,
2714f89e4b8SPaul Cercueil struct clk_hw_onecell_data *clocks)
2724f89e4b8SPaul Cercueil {
2734f89e4b8SPaul Cercueil struct ingenic_tcu_clk *tcu_clk;
2744f89e4b8SPaul Cercueil int err;
2754f89e4b8SPaul Cercueil
2764f89e4b8SPaul Cercueil tcu_clk = kzalloc(sizeof(*tcu_clk), GFP_KERNEL);
2774f89e4b8SPaul Cercueil if (!tcu_clk)
2784f89e4b8SPaul Cercueil return -ENOMEM;
2794f89e4b8SPaul Cercueil
2804f89e4b8SPaul Cercueil tcu_clk->hw.init = &info->init_data;
2814f89e4b8SPaul Cercueil tcu_clk->idx = idx;
2824f89e4b8SPaul Cercueil tcu_clk->info = info;
2834f89e4b8SPaul Cercueil tcu_clk->tcu = tcu;
2844f89e4b8SPaul Cercueil
2854f89e4b8SPaul Cercueil /* Reset channel and clock divider, set default parent */
2864f89e4b8SPaul Cercueil ingenic_tcu_enable_regs(&tcu_clk->hw);
2874f89e4b8SPaul Cercueil regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent));
2884f89e4b8SPaul Cercueil ingenic_tcu_disable_regs(&tcu_clk->hw);
2894f89e4b8SPaul Cercueil
2904f89e4b8SPaul Cercueil err = clk_hw_register(NULL, &tcu_clk->hw);
2914f89e4b8SPaul Cercueil if (err) {
2924f89e4b8SPaul Cercueil kfree(tcu_clk);
2934f89e4b8SPaul Cercueil return err;
2944f89e4b8SPaul Cercueil }
2954f89e4b8SPaul Cercueil
2964f89e4b8SPaul Cercueil clocks->hws[idx] = &tcu_clk->hw;
2974f89e4b8SPaul Cercueil
2984f89e4b8SPaul Cercueil return 0;
2994f89e4b8SPaul Cercueil }
3004f89e4b8SPaul Cercueil
3014f89e4b8SPaul Cercueil static const struct ingenic_soc_info jz4740_soc_info = {
3024f89e4b8SPaul Cercueil .num_channels = 8,
3034f89e4b8SPaul Cercueil .has_ost = false,
3044f89e4b8SPaul Cercueil .has_tcu_clk = true,
3054f89e4b8SPaul Cercueil };
3064f89e4b8SPaul Cercueil
3074f89e4b8SPaul Cercueil static const struct ingenic_soc_info jz4725b_soc_info = {
3084f89e4b8SPaul Cercueil .num_channels = 6,
3094f89e4b8SPaul Cercueil .has_ost = true,
3104f89e4b8SPaul Cercueil .has_tcu_clk = true,
3114f89e4b8SPaul Cercueil };
3124f89e4b8SPaul Cercueil
3134f89e4b8SPaul Cercueil static const struct ingenic_soc_info jz4770_soc_info = {
3144f89e4b8SPaul Cercueil .num_channels = 8,
3154f89e4b8SPaul Cercueil .has_ost = true,
3164f89e4b8SPaul Cercueil .has_tcu_clk = false,
3174f89e4b8SPaul Cercueil };
3184f89e4b8SPaul Cercueil
319dc6a81c3S周琰杰 (Zhou Yanjie) static const struct ingenic_soc_info x1000_soc_info = {
320dc6a81c3S周琰杰 (Zhou Yanjie) .num_channels = 8,
321dc6a81c3S周琰杰 (Zhou Yanjie) .has_ost = false, /* X1000 has OST, but it not belong TCU */
322e98839fbSAidan MacDonald .has_tcu_clk = true,
323e98839fbSAidan MacDonald .allow_missing_tcu_clk = true,
324dc6a81c3S周琰杰 (Zhou Yanjie) };
325dc6a81c3S周琰杰 (Zhou Yanjie)
326e480fe10SStephen Boyd static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = {
3274f89e4b8SPaul Cercueil { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },
3284f89e4b8SPaul Cercueil { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },
329bdbfc029SPaul Cercueil { .compatible = "ingenic,jz4760-tcu", .data = &jz4770_soc_info, },
3304f89e4b8SPaul Cercueil { .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },
331dc6a81c3S周琰杰 (Zhou Yanjie) { .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, },
3324f89e4b8SPaul Cercueil { /* sentinel */ }
3334f89e4b8SPaul Cercueil };
3344f89e4b8SPaul Cercueil
ingenic_tcu_probe(struct device_node * np)3354f89e4b8SPaul Cercueil static int __init ingenic_tcu_probe(struct device_node *np)
3364f89e4b8SPaul Cercueil {
3374f89e4b8SPaul Cercueil const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np);
3384f89e4b8SPaul Cercueil struct ingenic_tcu *tcu;
3394f89e4b8SPaul Cercueil struct regmap *map;
3404f89e4b8SPaul Cercueil unsigned int i;
3414f89e4b8SPaul Cercueil int ret;
3424f89e4b8SPaul Cercueil
3434f89e4b8SPaul Cercueil map = device_node_to_regmap(np);
3444f89e4b8SPaul Cercueil if (IS_ERR(map))
3454f89e4b8SPaul Cercueil return PTR_ERR(map);
3464f89e4b8SPaul Cercueil
3474f89e4b8SPaul Cercueil tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
3484f89e4b8SPaul Cercueil if (!tcu)
3494f89e4b8SPaul Cercueil return -ENOMEM;
3504f89e4b8SPaul Cercueil
3514f89e4b8SPaul Cercueil tcu->map = map;
3524f89e4b8SPaul Cercueil tcu->soc_info = id->data;
3534f89e4b8SPaul Cercueil
3544f89e4b8SPaul Cercueil if (tcu->soc_info->has_tcu_clk) {
3554f89e4b8SPaul Cercueil tcu->clk = of_clk_get_by_name(np, "tcu");
3564f89e4b8SPaul Cercueil if (IS_ERR(tcu->clk)) {
3574f89e4b8SPaul Cercueil ret = PTR_ERR(tcu->clk);
358e98839fbSAidan MacDonald
359e98839fbSAidan MacDonald /*
360e98839fbSAidan MacDonald * Old device trees for some SoCs did not include the
361e98839fbSAidan MacDonald * TCU clock because this driver (incorrectly) didn't
362e98839fbSAidan MacDonald * use it. In this case we complain loudly and attempt
363e98839fbSAidan MacDonald * to continue without the clock, which might work if
364e98839fbSAidan MacDonald * booting with workarounds like "clk_ignore_unused".
365e98839fbSAidan MacDonald */
366e98839fbSAidan MacDonald if (tcu->soc_info->allow_missing_tcu_clk && ret == -EINVAL) {
367e98839fbSAidan MacDonald pr_warn("TCU clock missing from device tree, please update your device tree\n");
368e98839fbSAidan MacDonald tcu->clk = NULL;
369e98839fbSAidan MacDonald } else {
370e98839fbSAidan MacDonald pr_crit("Cannot get TCU clock from device tree\n");
3714f89e4b8SPaul Cercueil goto err_free_tcu;
3724f89e4b8SPaul Cercueil }
373e98839fbSAidan MacDonald } else {
3744f89e4b8SPaul Cercueil ret = clk_prepare_enable(tcu->clk);
3754f89e4b8SPaul Cercueil if (ret) {
3764f89e4b8SPaul Cercueil pr_crit("Unable to enable TCU clock\n");
3774f89e4b8SPaul Cercueil goto err_put_clk;
3784f89e4b8SPaul Cercueil }
3794f89e4b8SPaul Cercueil }
380e98839fbSAidan MacDonald }
3814f89e4b8SPaul Cercueil
382e620a1e0SStephen Kitt tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT),
3834f89e4b8SPaul Cercueil GFP_KERNEL);
3844f89e4b8SPaul Cercueil if (!tcu->clocks) {
3854f89e4b8SPaul Cercueil ret = -ENOMEM;
3864f89e4b8SPaul Cercueil goto err_clk_disable;
3874f89e4b8SPaul Cercueil }
3884f89e4b8SPaul Cercueil
3894f89e4b8SPaul Cercueil tcu->clocks->num = TCU_CLK_COUNT;
3904f89e4b8SPaul Cercueil
3914f89e4b8SPaul Cercueil for (i = 0; i < tcu->soc_info->num_channels; i++) {
3924f89e4b8SPaul Cercueil ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT,
3934f89e4b8SPaul Cercueil &ingenic_tcu_clk_info[i],
3944f89e4b8SPaul Cercueil tcu->clocks);
3954f89e4b8SPaul Cercueil if (ret) {
3964f89e4b8SPaul Cercueil pr_crit("cannot register clock %d\n", i);
3974f89e4b8SPaul Cercueil goto err_unregister_timer_clocks;
3984f89e4b8SPaul Cercueil }
3994f89e4b8SPaul Cercueil }
4004f89e4b8SPaul Cercueil
4014f89e4b8SPaul Cercueil /*
4024f89e4b8SPaul Cercueil * We set EXT as the default parent clock for all the TCU clocks
4034f89e4b8SPaul Cercueil * except for the watchdog one, where we set the RTC clock as the
4044f89e4b8SPaul Cercueil * parent. Since the EXT and PCLK are much faster than the RTC clock,
4054f89e4b8SPaul Cercueil * the watchdog would kick after a maximum time of 5s, and we might
4064f89e4b8SPaul Cercueil * want a slower kicking time.
4074f89e4b8SPaul Cercueil */
4084f89e4b8SPaul Cercueil ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC,
4094f89e4b8SPaul Cercueil &ingenic_tcu_watchdog_clk_info,
4104f89e4b8SPaul Cercueil tcu->clocks);
4114f89e4b8SPaul Cercueil if (ret) {
4124f89e4b8SPaul Cercueil pr_crit("cannot register watchdog clock\n");
4134f89e4b8SPaul Cercueil goto err_unregister_timer_clocks;
4144f89e4b8SPaul Cercueil }
4154f89e4b8SPaul Cercueil
4164f89e4b8SPaul Cercueil if (tcu->soc_info->has_ost) {
4174f89e4b8SPaul Cercueil ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST,
4184f89e4b8SPaul Cercueil TCU_PARENT_EXT,
4194f89e4b8SPaul Cercueil &ingenic_tcu_ost_clk_info,
4204f89e4b8SPaul Cercueil tcu->clocks);
4214f89e4b8SPaul Cercueil if (ret) {
4224f89e4b8SPaul Cercueil pr_crit("cannot register ost clock\n");
4234f89e4b8SPaul Cercueil goto err_unregister_watchdog_clock;
4244f89e4b8SPaul Cercueil }
4254f89e4b8SPaul Cercueil }
4264f89e4b8SPaul Cercueil
4274f89e4b8SPaul Cercueil ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks);
4284f89e4b8SPaul Cercueil if (ret) {
4294f89e4b8SPaul Cercueil pr_crit("cannot add OF clock provider\n");
4304f89e4b8SPaul Cercueil goto err_unregister_ost_clock;
4314f89e4b8SPaul Cercueil }
4324f89e4b8SPaul Cercueil
4334f89e4b8SPaul Cercueil ingenic_tcu = tcu;
4344f89e4b8SPaul Cercueil
4354f89e4b8SPaul Cercueil return 0;
4364f89e4b8SPaul Cercueil
4374f89e4b8SPaul Cercueil err_unregister_ost_clock:
4384f89e4b8SPaul Cercueil if (tcu->soc_info->has_ost)
4394f89e4b8SPaul Cercueil clk_hw_unregister(tcu->clocks->hws[i + 1]);
4404f89e4b8SPaul Cercueil err_unregister_watchdog_clock:
4414f89e4b8SPaul Cercueil clk_hw_unregister(tcu->clocks->hws[i]);
4424f89e4b8SPaul Cercueil err_unregister_timer_clocks:
4434f89e4b8SPaul Cercueil for (i = 0; i < tcu->clocks->num; i++)
4444f89e4b8SPaul Cercueil if (tcu->clocks->hws[i])
4454f89e4b8SPaul Cercueil clk_hw_unregister(tcu->clocks->hws[i]);
4464f89e4b8SPaul Cercueil kfree(tcu->clocks);
4474f89e4b8SPaul Cercueil err_clk_disable:
448e98839fbSAidan MacDonald if (tcu->clk)
4494f89e4b8SPaul Cercueil clk_disable_unprepare(tcu->clk);
4504f89e4b8SPaul Cercueil err_put_clk:
451e98839fbSAidan MacDonald if (tcu->clk)
4524f89e4b8SPaul Cercueil clk_put(tcu->clk);
4534f89e4b8SPaul Cercueil err_free_tcu:
4544f89e4b8SPaul Cercueil kfree(tcu);
4554f89e4b8SPaul Cercueil return ret;
4564f89e4b8SPaul Cercueil }
4574f89e4b8SPaul Cercueil
tcu_pm_suspend(void)4584f89e4b8SPaul Cercueil static int __maybe_unused tcu_pm_suspend(void)
4594f89e4b8SPaul Cercueil {
4604f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = ingenic_tcu;
4614f89e4b8SPaul Cercueil
4624f89e4b8SPaul Cercueil if (tcu->clk)
4634f89e4b8SPaul Cercueil clk_disable(tcu->clk);
4644f89e4b8SPaul Cercueil
4654f89e4b8SPaul Cercueil return 0;
4664f89e4b8SPaul Cercueil }
4674f89e4b8SPaul Cercueil
tcu_pm_resume(void)4684f89e4b8SPaul Cercueil static void __maybe_unused tcu_pm_resume(void)
4694f89e4b8SPaul Cercueil {
4704f89e4b8SPaul Cercueil struct ingenic_tcu *tcu = ingenic_tcu;
4714f89e4b8SPaul Cercueil
4724f89e4b8SPaul Cercueil if (tcu->clk)
4734f89e4b8SPaul Cercueil clk_enable(tcu->clk);
4744f89e4b8SPaul Cercueil }
4754f89e4b8SPaul Cercueil
4764f89e4b8SPaul Cercueil static struct syscore_ops __maybe_unused tcu_pm_ops = {
4774f89e4b8SPaul Cercueil .suspend = tcu_pm_suspend,
4784f89e4b8SPaul Cercueil .resume = tcu_pm_resume,
4794f89e4b8SPaul Cercueil };
4804f89e4b8SPaul Cercueil
ingenic_tcu_init(struct device_node * np)4814f89e4b8SPaul Cercueil static void __init ingenic_tcu_init(struct device_node *np)
4824f89e4b8SPaul Cercueil {
4834f89e4b8SPaul Cercueil int ret = ingenic_tcu_probe(np);
4844f89e4b8SPaul Cercueil
4854f89e4b8SPaul Cercueil if (ret)
4864f89e4b8SPaul Cercueil pr_crit("Failed to initialize TCU clocks: %d\n", ret);
4874f89e4b8SPaul Cercueil
4884f89e4b8SPaul Cercueil if (IS_ENABLED(CONFIG_PM_SLEEP))
4894f89e4b8SPaul Cercueil register_syscore_ops(&tcu_pm_ops);
4904f89e4b8SPaul Cercueil }
4914f89e4b8SPaul Cercueil
4924f89e4b8SPaul Cercueil CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
4934f89e4b8SPaul Cercueil CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);
494bdbfc029SPaul Cercueil CLK_OF_DECLARE_DRIVER(jz4760_cgu, "ingenic,jz4760-tcu", ingenic_tcu_init);
4954f89e4b8SPaul Cercueil CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);
496dc6a81c3S周琰杰 (Zhou Yanjie) CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init);
497