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
This commit is contained in:
Родитель
5be8badcb6
Коммит
ca7b72b5a5
|
@ -697,6 +697,14 @@ config INGENIC_TIMER
|
|||
help
|
||||
Support for the timer/counter unit of the Ingenic JZ SoCs.
|
||||
|
||||
config INGENIC_OST
|
||||
bool "Clocksource for Ingenic OS Timer"
|
||||
depends on MIPS || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for the Operating System Timer of the Ingenic JZ SoCs.
|
||||
|
||||
config MICROCHIP_PIT64B
|
||||
bool "Microchip PIT64B support"
|
||||
depends on OF || COMPILE_TEST
|
||||
|
|
|
@ -80,6 +80,7 @@ obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
|
|||
obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
|
||||
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
|
||||
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
|
||||
obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o
|
||||
obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o
|
||||
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
|
||||
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* JZ47xx SoCs TCU Operating System Timer driver
|
||||
*
|
||||
* Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org>
|
||||
* Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/mfd/ingenic-tcu.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#define TCU_OST_TCSR_MASK 0xffc0
|
||||
#define TCU_OST_TCSR_CNT_MD BIT(15)
|
||||
|
||||
#define TCU_OST_CHANNEL 15
|
||||
|
||||
/*
|
||||
* The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the
|
||||
* regmap; these are for use with the __iomem pointer.
|
||||
*/
|
||||
#define OST_REG_CNTL 0x4
|
||||
#define OST_REG_CNTH 0x8
|
||||
|
||||
struct ingenic_ost_soc_info {
|
||||
bool is64bit;
|
||||
};
|
||||
|
||||
struct ingenic_ost {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
struct clocksource cs;
|
||||
};
|
||||
|
||||
static struct ingenic_ost *ingenic_ost;
|
||||
|
||||
static u64 notrace ingenic_ost_read_cntl(void)
|
||||
{
|
||||
/* Read using __iomem pointer instead of regmap to avoid locking */
|
||||
return readl(ingenic_ost->regs + OST_REG_CNTL);
|
||||
}
|
||||
|
||||
static u64 notrace ingenic_ost_read_cnth(void)
|
||||
{
|
||||
/* Read using __iomem pointer instead of regmap to avoid locking */
|
||||
return readl(ingenic_ost->regs + OST_REG_CNTH);
|
||||
}
|
||||
|
||||
static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs)
|
||||
{
|
||||
return ingenic_ost_read_cntl();
|
||||
}
|
||||
|
||||
static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs)
|
||||
{
|
||||
return ingenic_ost_read_cnth();
|
||||
}
|
||||
|
||||
static int __init ingenic_ost_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ingenic_ost_soc_info *soc_info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ingenic_ost *ost;
|
||||
struct clocksource *cs;
|
||||
struct regmap *map;
|
||||
unsigned long rate;
|
||||
int err;
|
||||
|
||||
soc_info = device_get_match_data(dev);
|
||||
if (!soc_info)
|
||||
return -EINVAL;
|
||||
|
||||
ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL);
|
||||
if (!ost)
|
||||
return -ENOMEM;
|
||||
|
||||
ingenic_ost = ost;
|
||||
|
||||
ost->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ost->regs))
|
||||
return PTR_ERR(ost->regs);
|
||||
|
||||
map = device_node_to_regmap(dev->parent->of_node);
|
||||
if (!map) {
|
||||
dev_err(dev, "regmap not found");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ost->clk = devm_clk_get(dev, "ost");
|
||||
if (IS_ERR(ost->clk))
|
||||
return PTR_ERR(ost->clk);
|
||||
|
||||
err = clk_prepare_enable(ost->clk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Clear counter high/low registers */
|
||||
if (soc_info->is64bit)
|
||||
regmap_write(map, TCU_REG_OST_CNTL, 0);
|
||||
regmap_write(map, TCU_REG_OST_CNTH, 0);
|
||||
|
||||
/* Don't reset counter at compare value. */
|
||||
regmap_update_bits(map, TCU_REG_OST_TCSR,
|
||||
TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD);
|
||||
|
||||
rate = clk_get_rate(ost->clk);
|
||||
|
||||
/* Enable OST TCU channel */
|
||||
regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL));
|
||||
|
||||
cs = &ost->cs;
|
||||
cs->name = "ingenic-ost";
|
||||
cs->rating = 320;
|
||||
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
||||
cs->mask = CLOCKSOURCE_MASK(32);
|
||||
|
||||
if (soc_info->is64bit)
|
||||
cs->read = ingenic_ost_clocksource_readl;
|
||||
else
|
||||
cs->read = ingenic_ost_clocksource_readh;
|
||||
|
||||
err = clocksource_register_hz(cs, rate);
|
||||
if (err) {
|
||||
dev_err(dev, "clocksource registration failed");
|
||||
clk_disable_unprepare(ost->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (soc_info->is64bit)
|
||||
sched_clock_register(ingenic_ost_read_cntl, 32, rate);
|
||||
else
|
||||
sched_clock_register(ingenic_ost_read_cnth, 32, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ingenic_ost_suspend(struct device *dev)
|
||||
{
|
||||
struct ingenic_ost *ost = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(ost->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ingenic_ost_resume(struct device *dev)
|
||||
{
|
||||
struct ingenic_ost *ost = dev_get_drvdata(dev);
|
||||
|
||||
return clk_enable(ost->clk);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = {
|
||||
/* _noirq: We want the OST clock to be gated last / ungated first */
|
||||
.suspend_noirq = ingenic_ost_suspend,
|
||||
.resume_noirq = ingenic_ost_resume,
|
||||
};
|
||||
|
||||
static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = {
|
||||
.is64bit = false,
|
||||
};
|
||||
|
||||
static const struct ingenic_ost_soc_info jz4770_ost_soc_info = {
|
||||
.is64bit = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id ingenic_ost_of_match[] = {
|
||||
{ .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, },
|
||||
{ .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ingenic_ost_driver = {
|
||||
.driver = {
|
||||
.name = "ingenic-ost",
|
||||
#ifdef CONFIG_PM_SUSPEND
|
||||
.pm = &ingenic_ost_pm_ops,
|
||||
#endif
|
||||
.of_match_table = ingenic_ost_of_match,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);
|
Загрузка…
Ссылка в новой задаче