cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ingenic-ost.c (4576B)


      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
     31struct ingenic_ost_soc_info {
     32	bool is64bit;
     33};
     34
     35struct ingenic_ost {
     36	void __iomem *regs;
     37	struct clk *clk;
     38
     39	struct clocksource cs;
     40};
     41
     42static struct ingenic_ost *ingenic_ost;
     43
     44static 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
     50static 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
     56static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs)
     57{
     58	return ingenic_ost_read_cntl();
     59}
     60
     61static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs)
     62{
     63	return ingenic_ost_read_cnth();
     64}
     65
     66static 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 (IS_ERR(map)) {
     92		dev_err(dev, "regmap not found");
     93		return PTR_ERR(map);
     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
    144static 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
    153static 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
    160static 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
    166static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = {
    167	.is64bit = false,
    168};
    169
    170static const struct ingenic_ost_soc_info jz4760b_ost_soc_info = {
    171	.is64bit = true,
    172};
    173
    174static const struct of_device_id ingenic_ost_of_match[] = {
    175	{ .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, },
    176	{ .compatible = "ingenic,jz4760b-ost", .data = &jz4760b_ost_soc_info, },
    177	{ .compatible = "ingenic,jz4770-ost", .data = &jz4760b_ost_soc_info, },
    178	{ }
    179};
    180
    181static struct platform_driver ingenic_ost_driver = {
    182	.driver = {
    183		.name = "ingenic-ost",
    184#ifdef CONFIG_PM_SUSPEND
    185		.pm = &ingenic_ost_pm_ops,
    186#endif
    187		.of_match_table = ingenic_ost_of_match,
    188	},
    189};
    190builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);