Skip to content

Commit ca7b72b

Browse files
mthuurnedlezcano
authored andcommitted
clocksource: Add driver for the Ingenic JZ47xx OST
OST is the OS Timer, a 64-bit timer/counter with buffered reading. SoCs before the JZ4770 had (if any) a 32-bit OST; the JZ4770 and JZ4780 have a 64-bit OST. This driver will register both a clocksource and a sched_clock to the system. Signed-off-by: Maarten ter Huurne <maarten@treewalker.org> Signed-off-by: Paul Cercueil <paul@crapouillou.net> Tested-by: Mathieu Malaterre <malat@debian.org> Tested-by: Artur Rojek <contact@artur-rojek.eu> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Link: https://lore.kernel.org/r/20200212180408.30872-1-paul@crapouillou.net
1 parent 5be8bad commit ca7b72b

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

drivers/clocksource/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,14 @@ config INGENIC_TIMER
697697
help
698698
Support for the timer/counter unit of the Ingenic JZ SoCs.
699699

700+
config INGENIC_OST
701+
bool "Clocksource for Ingenic OS Timer"
702+
depends on MIPS || COMPILE_TEST
703+
depends on COMMON_CLK
704+
select MFD_SYSCON
705+
help
706+
Support for the Operating System Timer of the Ingenic JZ SoCs.
707+
700708
config MICROCHIP_PIT64B
701709
bool "Microchip PIT64B support"
702710
depends on OF || COMPILE_TEST

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
8080
obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
8181
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
8282
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
83+
obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o
8384
obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o
8485
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
8586
obj-$(CONFIG_X86_NUMACHIP) += numachip.o

drivers/clocksource/ingenic-ost.c

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

0 commit comments

Comments
 (0)