1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * JZ47xx SoCs TCU Operating System Timer driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org>
6*4882a593Smuzhiyun * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/clocksource.h>
11*4882a593Smuzhiyun #include <linux/mfd/ingenic-tcu.h>
12*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
13*4882a593Smuzhiyun #include <linux/of.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/pm.h>
16*4882a593Smuzhiyun #include <linux/regmap.h>
17*4882a593Smuzhiyun #include <linux/sched_clock.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define TCU_OST_TCSR_MASK 0xffc0
20*4882a593Smuzhiyun #define TCU_OST_TCSR_CNT_MD BIT(15)
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define TCU_OST_CHANNEL 15
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /*
25*4882a593Smuzhiyun * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the
26*4882a593Smuzhiyun * regmap; these are for use with the __iomem pointer.
27*4882a593Smuzhiyun */
28*4882a593Smuzhiyun #define OST_REG_CNTL 0x4
29*4882a593Smuzhiyun #define OST_REG_CNTH 0x8
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun struct ingenic_ost_soc_info {
32*4882a593Smuzhiyun bool is64bit;
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun struct ingenic_ost {
36*4882a593Smuzhiyun void __iomem *regs;
37*4882a593Smuzhiyun struct clk *clk;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct clocksource cs;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static struct ingenic_ost *ingenic_ost;
43*4882a593Smuzhiyun
ingenic_ost_read_cntl(void)44*4882a593Smuzhiyun static u64 notrace ingenic_ost_read_cntl(void)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun /* Read using __iomem pointer instead of regmap to avoid locking */
47*4882a593Smuzhiyun return readl(ingenic_ost->regs + OST_REG_CNTL);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
ingenic_ost_read_cnth(void)50*4882a593Smuzhiyun static u64 notrace ingenic_ost_read_cnth(void)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun /* Read using __iomem pointer instead of regmap to avoid locking */
53*4882a593Smuzhiyun return readl(ingenic_ost->regs + OST_REG_CNTH);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
ingenic_ost_clocksource_readl(struct clocksource * cs)56*4882a593Smuzhiyun static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun return ingenic_ost_read_cntl();
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
ingenic_ost_clocksource_readh(struct clocksource * cs)61*4882a593Smuzhiyun static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun return ingenic_ost_read_cnth();
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
ingenic_ost_probe(struct platform_device * pdev)66*4882a593Smuzhiyun static int __init ingenic_ost_probe(struct platform_device *pdev)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun const struct ingenic_ost_soc_info *soc_info;
69*4882a593Smuzhiyun struct device *dev = &pdev->dev;
70*4882a593Smuzhiyun struct ingenic_ost *ost;
71*4882a593Smuzhiyun struct clocksource *cs;
72*4882a593Smuzhiyun struct regmap *map;
73*4882a593Smuzhiyun unsigned long rate;
74*4882a593Smuzhiyun int err;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun soc_info = device_get_match_data(dev);
77*4882a593Smuzhiyun if (!soc_info)
78*4882a593Smuzhiyun return -EINVAL;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL);
81*4882a593Smuzhiyun if (!ost)
82*4882a593Smuzhiyun return -ENOMEM;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun ingenic_ost = ost;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun ost->regs = devm_platform_ioremap_resource(pdev, 0);
87*4882a593Smuzhiyun if (IS_ERR(ost->regs))
88*4882a593Smuzhiyun return PTR_ERR(ost->regs);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun map = device_node_to_regmap(dev->parent->of_node);
91*4882a593Smuzhiyun if (IS_ERR(map)) {
92*4882a593Smuzhiyun dev_err(dev, "regmap not found");
93*4882a593Smuzhiyun return PTR_ERR(map);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun ost->clk = devm_clk_get(dev, "ost");
97*4882a593Smuzhiyun if (IS_ERR(ost->clk))
98*4882a593Smuzhiyun return PTR_ERR(ost->clk);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun err = clk_prepare_enable(ost->clk);
101*4882a593Smuzhiyun if (err)
102*4882a593Smuzhiyun return err;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /* Clear counter high/low registers */
105*4882a593Smuzhiyun if (soc_info->is64bit)
106*4882a593Smuzhiyun regmap_write(map, TCU_REG_OST_CNTL, 0);
107*4882a593Smuzhiyun regmap_write(map, TCU_REG_OST_CNTH, 0);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /* Don't reset counter at compare value. */
110*4882a593Smuzhiyun regmap_update_bits(map, TCU_REG_OST_TCSR,
111*4882a593Smuzhiyun TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun rate = clk_get_rate(ost->clk);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun /* Enable OST TCU channel */
116*4882a593Smuzhiyun regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL));
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun cs = &ost->cs;
119*4882a593Smuzhiyun cs->name = "ingenic-ost";
120*4882a593Smuzhiyun cs->rating = 320;
121*4882a593Smuzhiyun cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
122*4882a593Smuzhiyun cs->mask = CLOCKSOURCE_MASK(32);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (soc_info->is64bit)
125*4882a593Smuzhiyun cs->read = ingenic_ost_clocksource_readl;
126*4882a593Smuzhiyun else
127*4882a593Smuzhiyun cs->read = ingenic_ost_clocksource_readh;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun err = clocksource_register_hz(cs, rate);
130*4882a593Smuzhiyun if (err) {
131*4882a593Smuzhiyun dev_err(dev, "clocksource registration failed");
132*4882a593Smuzhiyun clk_disable_unprepare(ost->clk);
133*4882a593Smuzhiyun return err;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (soc_info->is64bit)
137*4882a593Smuzhiyun sched_clock_register(ingenic_ost_read_cntl, 32, rate);
138*4882a593Smuzhiyun else
139*4882a593Smuzhiyun sched_clock_register(ingenic_ost_read_cnth, 32, rate);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun return 0;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
ingenic_ost_suspend(struct device * dev)144*4882a593Smuzhiyun static int __maybe_unused ingenic_ost_suspend(struct device *dev)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct ingenic_ost *ost = dev_get_drvdata(dev);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun clk_disable(ost->clk);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
ingenic_ost_resume(struct device * dev)153*4882a593Smuzhiyun static int __maybe_unused ingenic_ost_resume(struct device *dev)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun struct ingenic_ost *ost = dev_get_drvdata(dev);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun return clk_enable(ost->clk);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = {
161*4882a593Smuzhiyun /* _noirq: We want the OST clock to be gated last / ungated first */
162*4882a593Smuzhiyun .suspend_noirq = ingenic_ost_suspend,
163*4882a593Smuzhiyun .resume_noirq = ingenic_ost_resume,
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = {
167*4882a593Smuzhiyun .is64bit = false,
168*4882a593Smuzhiyun };
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun static const struct ingenic_ost_soc_info jz4770_ost_soc_info = {
171*4882a593Smuzhiyun .is64bit = true,
172*4882a593Smuzhiyun };
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun static const struct of_device_id ingenic_ost_of_match[] = {
175*4882a593Smuzhiyun { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, },
176*4882a593Smuzhiyun { .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, },
177*4882a593Smuzhiyun { }
178*4882a593Smuzhiyun };
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun static struct platform_driver ingenic_ost_driver = {
181*4882a593Smuzhiyun .driver = {
182*4882a593Smuzhiyun .name = "ingenic-ost",
183*4882a593Smuzhiyun #ifdef CONFIG_PM_SUSPEND
184*4882a593Smuzhiyun .pm = &ingenic_ost_pm_ops,
185*4882a593Smuzhiyun #endif
186*4882a593Smuzhiyun .of_match_table = ingenic_ost_of_match,
187*4882a593Smuzhiyun },
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);
190