1 // SPDX-License-Identifier: GPL-2.0
3 * JZ47xx SoCs TCU Operating System Timer driver
5 * Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org>
6 * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
10 #include <linux/clocksource.h>
11 #include <linux/mfd/ingenic-tcu.h>
12 #include <linux/mfd/syscon.h>
14 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17 #include <linux/sched_clock.h>
19 #define TCU_OST_TCSR_MASK 0xffc0
20 #define TCU_OST_TCSR_CNT_MD BIT(15)
22 #define TCU_OST_CHANNEL 15
25 * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the
26 * regmap; these are for use with the __iomem pointer.
28 #define OST_REG_CNTL 0x4
29 #define OST_REG_CNTH 0x8
31 struct ingenic_ost_soc_info
{
39 struct clocksource cs
;
42 static struct ingenic_ost
*ingenic_ost
;
44 static u64 notrace
ingenic_ost_read_cntl(void)
46 /* Read using __iomem pointer instead of regmap to avoid locking */
47 return readl(ingenic_ost
->regs
+ OST_REG_CNTL
);
50 static u64 notrace
ingenic_ost_read_cnth(void)
52 /* Read using __iomem pointer instead of regmap to avoid locking */
53 return readl(ingenic_ost
->regs
+ OST_REG_CNTH
);
56 static u64 notrace
ingenic_ost_clocksource_readl(struct clocksource
*cs
)
58 return ingenic_ost_read_cntl();
61 static u64 notrace
ingenic_ost_clocksource_readh(struct clocksource
*cs
)
63 return ingenic_ost_read_cnth();
66 static int __init
ingenic_ost_probe(struct platform_device
*pdev
)
68 const struct ingenic_ost_soc_info
*soc_info
;
69 struct device
*dev
= &pdev
->dev
;
70 struct ingenic_ost
*ost
;
71 struct clocksource
*cs
;
76 soc_info
= device_get_match_data(dev
);
80 ost
= devm_kzalloc(dev
, sizeof(*ost
), GFP_KERNEL
);
86 ost
->regs
= devm_platform_ioremap_resource(pdev
, 0);
87 if (IS_ERR(ost
->regs
))
88 return PTR_ERR(ost
->regs
);
90 map
= device_node_to_regmap(dev
->parent
->of_node
);
92 dev_err(dev
, "regmap not found");
96 ost
->clk
= devm_clk_get(dev
, "ost");
98 return PTR_ERR(ost
->clk
);
100 err
= clk_prepare_enable(ost
->clk
);
104 /* Clear counter high/low registers */
105 if (soc_info
->is64bit
)
106 regmap_write(map
, TCU_REG_OST_CNTL
, 0);
107 regmap_write(map
, TCU_REG_OST_CNTH
, 0);
109 /* Don't reset counter at compare value. */
110 regmap_update_bits(map
, TCU_REG_OST_TCSR
,
111 TCU_OST_TCSR_MASK
, TCU_OST_TCSR_CNT_MD
);
113 rate
= clk_get_rate(ost
->clk
);
115 /* Enable OST TCU channel */
116 regmap_write(map
, TCU_REG_TESR
, BIT(TCU_OST_CHANNEL
));
119 cs
->name
= "ingenic-ost";
121 cs
->flags
= CLOCK_SOURCE_IS_CONTINUOUS
;
122 cs
->mask
= CLOCKSOURCE_MASK(32);
124 if (soc_info
->is64bit
)
125 cs
->read
= ingenic_ost_clocksource_readl
;
127 cs
->read
= ingenic_ost_clocksource_readh
;
129 err
= clocksource_register_hz(cs
, rate
);
131 dev_err(dev
, "clocksource registration failed");
132 clk_disable_unprepare(ost
->clk
);
136 if (soc_info
->is64bit
)
137 sched_clock_register(ingenic_ost_read_cntl
, 32, rate
);
139 sched_clock_register(ingenic_ost_read_cnth
, 32, rate
);
144 static int __maybe_unused
ingenic_ost_suspend(struct device
*dev
)
146 struct ingenic_ost
*ost
= dev_get_drvdata(dev
);
148 clk_disable(ost
->clk
);
153 static int __maybe_unused
ingenic_ost_resume(struct device
*dev
)
155 struct ingenic_ost
*ost
= dev_get_drvdata(dev
);
157 return clk_enable(ost
->clk
);
160 static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops
= {
161 /* _noirq: We want the OST clock to be gated last / ungated first */
162 .suspend_noirq
= ingenic_ost_suspend
,
163 .resume_noirq
= ingenic_ost_resume
,
166 static const struct ingenic_ost_soc_info jz4725b_ost_soc_info
= {
170 static const struct ingenic_ost_soc_info jz4770_ost_soc_info
= {
174 static const struct of_device_id ingenic_ost_of_match
[] = {
175 { .compatible
= "ingenic,jz4725b-ost", .data
= &jz4725b_ost_soc_info
, },
176 { .compatible
= "ingenic,jz4770-ost", .data
= &jz4770_ost_soc_info
, },
180 static struct platform_driver ingenic_ost_driver
= {
182 .name
= "ingenic-ost",
183 #ifdef CONFIG_PM_SUSPEND
184 .pm
= &ingenic_ost_pm_ops
,
186 .of_match_table
= ingenic_ost_of_match
,
189 builtin_platform_driver_probe(ingenic_ost_driver
, ingenic_ost_probe
);