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

em_sti.c (8615B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Emma Mobile Timer Support - STI
      4 *
      5 *  Copyright (C) 2012 Magnus Damm
      6 */
      7
      8#include <linux/init.h>
      9#include <linux/platform_device.h>
     10#include <linux/spinlock.h>
     11#include <linux/interrupt.h>
     12#include <linux/ioport.h>
     13#include <linux/io.h>
     14#include <linux/clk.h>
     15#include <linux/irq.h>
     16#include <linux/err.h>
     17#include <linux/delay.h>
     18#include <linux/clocksource.h>
     19#include <linux/clockchips.h>
     20#include <linux/slab.h>
     21#include <linux/module.h>
     22
     23enum { USER_CLOCKSOURCE, USER_CLOCKEVENT, USER_NR };
     24
     25struct em_sti_priv {
     26	void __iomem *base;
     27	struct clk *clk;
     28	struct platform_device *pdev;
     29	unsigned int active[USER_NR];
     30	unsigned long rate;
     31	raw_spinlock_t lock;
     32	struct clock_event_device ced;
     33	struct clocksource cs;
     34};
     35
     36#define STI_CONTROL 0x00
     37#define STI_COMPA_H 0x10
     38#define STI_COMPA_L 0x14
     39#define STI_COMPB_H 0x18
     40#define STI_COMPB_L 0x1c
     41#define STI_COUNT_H 0x20
     42#define STI_COUNT_L 0x24
     43#define STI_COUNT_RAW_H 0x28
     44#define STI_COUNT_RAW_L 0x2c
     45#define STI_SET_H 0x30
     46#define STI_SET_L 0x34
     47#define STI_INTSTATUS 0x40
     48#define STI_INTRAWSTATUS 0x44
     49#define STI_INTENSET 0x48
     50#define STI_INTENCLR 0x4c
     51#define STI_INTFFCLR 0x50
     52
     53static inline unsigned long em_sti_read(struct em_sti_priv *p, int offs)
     54{
     55	return ioread32(p->base + offs);
     56}
     57
     58static inline void em_sti_write(struct em_sti_priv *p, int offs,
     59				unsigned long value)
     60{
     61	iowrite32(value, p->base + offs);
     62}
     63
     64static int em_sti_enable(struct em_sti_priv *p)
     65{
     66	int ret;
     67
     68	/* enable clock */
     69	ret = clk_enable(p->clk);
     70	if (ret) {
     71		dev_err(&p->pdev->dev, "cannot enable clock\n");
     72		return ret;
     73	}
     74
     75	/* reset the counter */
     76	em_sti_write(p, STI_SET_H, 0x40000000);
     77	em_sti_write(p, STI_SET_L, 0x00000000);
     78
     79	/* mask and clear pending interrupts */
     80	em_sti_write(p, STI_INTENCLR, 3);
     81	em_sti_write(p, STI_INTFFCLR, 3);
     82
     83	/* enable updates of counter registers */
     84	em_sti_write(p, STI_CONTROL, 1);
     85
     86	return 0;
     87}
     88
     89static void em_sti_disable(struct em_sti_priv *p)
     90{
     91	/* mask interrupts */
     92	em_sti_write(p, STI_INTENCLR, 3);
     93
     94	/* stop clock */
     95	clk_disable(p->clk);
     96}
     97
     98static u64 em_sti_count(struct em_sti_priv *p)
     99{
    100	u64 ticks;
    101	unsigned long flags;
    102
    103	/* the STI hardware buffers the 48-bit count, but to
    104	 * break it out into two 32-bit access the registers
    105	 * must be accessed in a certain order.
    106	 * Always read STI_COUNT_H before STI_COUNT_L.
    107	 */
    108	raw_spin_lock_irqsave(&p->lock, flags);
    109	ticks = (u64)(em_sti_read(p, STI_COUNT_H) & 0xffff) << 32;
    110	ticks |= em_sti_read(p, STI_COUNT_L);
    111	raw_spin_unlock_irqrestore(&p->lock, flags);
    112
    113	return ticks;
    114}
    115
    116static u64 em_sti_set_next(struct em_sti_priv *p, u64 next)
    117{
    118	unsigned long flags;
    119
    120	raw_spin_lock_irqsave(&p->lock, flags);
    121
    122	/* mask compare A interrupt */
    123	em_sti_write(p, STI_INTENCLR, 1);
    124
    125	/* update compare A value */
    126	em_sti_write(p, STI_COMPA_H, next >> 32);
    127	em_sti_write(p, STI_COMPA_L, next & 0xffffffff);
    128
    129	/* clear compare A interrupt source */
    130	em_sti_write(p, STI_INTFFCLR, 1);
    131
    132	/* unmask compare A interrupt */
    133	em_sti_write(p, STI_INTENSET, 1);
    134
    135	raw_spin_unlock_irqrestore(&p->lock, flags);
    136
    137	return next;
    138}
    139
    140static irqreturn_t em_sti_interrupt(int irq, void *dev_id)
    141{
    142	struct em_sti_priv *p = dev_id;
    143
    144	p->ced.event_handler(&p->ced);
    145	return IRQ_HANDLED;
    146}
    147
    148static int em_sti_start(struct em_sti_priv *p, unsigned int user)
    149{
    150	unsigned long flags;
    151	int used_before;
    152	int ret = 0;
    153
    154	raw_spin_lock_irqsave(&p->lock, flags);
    155	used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
    156	if (!used_before)
    157		ret = em_sti_enable(p);
    158
    159	if (!ret)
    160		p->active[user] = 1;
    161	raw_spin_unlock_irqrestore(&p->lock, flags);
    162
    163	return ret;
    164}
    165
    166static void em_sti_stop(struct em_sti_priv *p, unsigned int user)
    167{
    168	unsigned long flags;
    169	int used_before, used_after;
    170
    171	raw_spin_lock_irqsave(&p->lock, flags);
    172	used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
    173	p->active[user] = 0;
    174	used_after = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
    175
    176	if (used_before && !used_after)
    177		em_sti_disable(p);
    178	raw_spin_unlock_irqrestore(&p->lock, flags);
    179}
    180
    181static struct em_sti_priv *cs_to_em_sti(struct clocksource *cs)
    182{
    183	return container_of(cs, struct em_sti_priv, cs);
    184}
    185
    186static u64 em_sti_clocksource_read(struct clocksource *cs)
    187{
    188	return em_sti_count(cs_to_em_sti(cs));
    189}
    190
    191static int em_sti_clocksource_enable(struct clocksource *cs)
    192{
    193	struct em_sti_priv *p = cs_to_em_sti(cs);
    194
    195	return em_sti_start(p, USER_CLOCKSOURCE);
    196}
    197
    198static void em_sti_clocksource_disable(struct clocksource *cs)
    199{
    200	em_sti_stop(cs_to_em_sti(cs), USER_CLOCKSOURCE);
    201}
    202
    203static void em_sti_clocksource_resume(struct clocksource *cs)
    204{
    205	em_sti_clocksource_enable(cs);
    206}
    207
    208static int em_sti_register_clocksource(struct em_sti_priv *p)
    209{
    210	struct clocksource *cs = &p->cs;
    211
    212	cs->name = dev_name(&p->pdev->dev);
    213	cs->rating = 200;
    214	cs->read = em_sti_clocksource_read;
    215	cs->enable = em_sti_clocksource_enable;
    216	cs->disable = em_sti_clocksource_disable;
    217	cs->suspend = em_sti_clocksource_disable;
    218	cs->resume = em_sti_clocksource_resume;
    219	cs->mask = CLOCKSOURCE_MASK(48);
    220	cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
    221
    222	dev_info(&p->pdev->dev, "used as clock source\n");
    223
    224	clocksource_register_hz(cs, p->rate);
    225	return 0;
    226}
    227
    228static struct em_sti_priv *ced_to_em_sti(struct clock_event_device *ced)
    229{
    230	return container_of(ced, struct em_sti_priv, ced);
    231}
    232
    233static int em_sti_clock_event_shutdown(struct clock_event_device *ced)
    234{
    235	struct em_sti_priv *p = ced_to_em_sti(ced);
    236	em_sti_stop(p, USER_CLOCKEVENT);
    237	return 0;
    238}
    239
    240static int em_sti_clock_event_set_oneshot(struct clock_event_device *ced)
    241{
    242	struct em_sti_priv *p = ced_to_em_sti(ced);
    243
    244	dev_info(&p->pdev->dev, "used for oneshot clock events\n");
    245	em_sti_start(p, USER_CLOCKEVENT);
    246	return 0;
    247}
    248
    249static int em_sti_clock_event_next(unsigned long delta,
    250				   struct clock_event_device *ced)
    251{
    252	struct em_sti_priv *p = ced_to_em_sti(ced);
    253	u64 next;
    254	int safe;
    255
    256	next = em_sti_set_next(p, em_sti_count(p) + delta);
    257	safe = em_sti_count(p) < (next - 1);
    258
    259	return !safe;
    260}
    261
    262static void em_sti_register_clockevent(struct em_sti_priv *p)
    263{
    264	struct clock_event_device *ced = &p->ced;
    265
    266	ced->name = dev_name(&p->pdev->dev);
    267	ced->features = CLOCK_EVT_FEAT_ONESHOT;
    268	ced->rating = 200;
    269	ced->cpumask = cpu_possible_mask;
    270	ced->set_next_event = em_sti_clock_event_next;
    271	ced->set_state_shutdown = em_sti_clock_event_shutdown;
    272	ced->set_state_oneshot = em_sti_clock_event_set_oneshot;
    273
    274	dev_info(&p->pdev->dev, "used for clock events\n");
    275
    276	clockevents_config_and_register(ced, p->rate, 2, 0xffffffff);
    277}
    278
    279static int em_sti_probe(struct platform_device *pdev)
    280{
    281	struct em_sti_priv *p;
    282	int irq, ret;
    283
    284	p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
    285	if (p == NULL)
    286		return -ENOMEM;
    287
    288	p->pdev = pdev;
    289	platform_set_drvdata(pdev, p);
    290
    291	irq = platform_get_irq(pdev, 0);
    292	if (irq < 0)
    293		return irq;
    294
    295	/* map memory, let base point to the STI instance */
    296	p->base = devm_platform_ioremap_resource(pdev, 0);
    297	if (IS_ERR(p->base))
    298		return PTR_ERR(p->base);
    299
    300	ret = devm_request_irq(&pdev->dev, irq, em_sti_interrupt,
    301			       IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
    302			       dev_name(&pdev->dev), p);
    303	if (ret) {
    304		dev_err(&pdev->dev, "failed to request low IRQ\n");
    305		return ret;
    306	}
    307
    308	/* get hold of clock */
    309	p->clk = devm_clk_get(&pdev->dev, "sclk");
    310	if (IS_ERR(p->clk)) {
    311		dev_err(&pdev->dev, "cannot get clock\n");
    312		return PTR_ERR(p->clk);
    313	}
    314
    315	ret = clk_prepare(p->clk);
    316	if (ret < 0) {
    317		dev_err(&pdev->dev, "cannot prepare clock\n");
    318		return ret;
    319	}
    320
    321	ret = clk_enable(p->clk);
    322	if (ret < 0) {
    323		dev_err(&p->pdev->dev, "cannot enable clock\n");
    324		clk_unprepare(p->clk);
    325		return ret;
    326	}
    327	p->rate = clk_get_rate(p->clk);
    328	clk_disable(p->clk);
    329
    330	raw_spin_lock_init(&p->lock);
    331	em_sti_register_clockevent(p);
    332	em_sti_register_clocksource(p);
    333	return 0;
    334}
    335
    336static int em_sti_remove(struct platform_device *pdev)
    337{
    338	return -EBUSY; /* cannot unregister clockevent and clocksource */
    339}
    340
    341static const struct of_device_id em_sti_dt_ids[] = {
    342	{ .compatible = "renesas,em-sti", },
    343	{},
    344};
    345MODULE_DEVICE_TABLE(of, em_sti_dt_ids);
    346
    347static struct platform_driver em_sti_device_driver = {
    348	.probe		= em_sti_probe,
    349	.remove		= em_sti_remove,
    350	.driver		= {
    351		.name	= "em_sti",
    352		.of_match_table = em_sti_dt_ids,
    353	}
    354};
    355
    356static int __init em_sti_init(void)
    357{
    358	return platform_driver_register(&em_sti_device_driver);
    359}
    360
    361static void __exit em_sti_exit(void)
    362{
    363	platform_driver_unregister(&em_sti_device_driver);
    364}
    365
    366subsys_initcall(em_sti_init);
    367module_exit(em_sti_exit);
    368
    369MODULE_AUTHOR("Magnus Damm");
    370MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver");
    371MODULE_LICENSE("GPL v2");