xref: /OK3568_Linux_fs/kernel/drivers/clocksource/ingenic-ost.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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