10b712183SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b9dbf951SMagnus Damm /* 3b9dbf951SMagnus Damm * Emma Mobile Timer Support - STI 4b9dbf951SMagnus Damm * 5b9dbf951SMagnus Damm * Copyright (C) 2012 Magnus Damm 6b9dbf951SMagnus Damm */ 7b9dbf951SMagnus Damm 8b9dbf951SMagnus Damm #include <linux/init.h> 9b9dbf951SMagnus Damm #include <linux/platform_device.h> 10b9dbf951SMagnus Damm #include <linux/spinlock.h> 11b9dbf951SMagnus Damm #include <linux/interrupt.h> 12b9dbf951SMagnus Damm #include <linux/ioport.h> 13b9dbf951SMagnus Damm #include <linux/io.h> 14b9dbf951SMagnus Damm #include <linux/clk.h> 15b9dbf951SMagnus Damm #include <linux/irq.h> 16b9dbf951SMagnus Damm #include <linux/err.h> 17b9dbf951SMagnus Damm #include <linux/delay.h> 18b9dbf951SMagnus Damm #include <linux/clocksource.h> 19b9dbf951SMagnus Damm #include <linux/clockchips.h> 20b9dbf951SMagnus Damm #include <linux/slab.h> 21b9dbf951SMagnus Damm #include <linux/module.h> 22b9dbf951SMagnus Damm 23b9dbf951SMagnus Damm enum { USER_CLOCKSOURCE, USER_CLOCKEVENT, USER_NR }; 24b9dbf951SMagnus Damm 25b9dbf951SMagnus Damm struct em_sti_priv { 26b9dbf951SMagnus Damm void __iomem *base; 27b9dbf951SMagnus Damm struct clk *clk; 28b9dbf951SMagnus Damm struct platform_device *pdev; 29b9dbf951SMagnus Damm unsigned int active[USER_NR]; 30b9dbf951SMagnus Damm unsigned long rate; 31b9dbf951SMagnus Damm raw_spinlock_t lock; 32b9dbf951SMagnus Damm struct clock_event_device ced; 33b9dbf951SMagnus Damm struct clocksource cs; 34b9dbf951SMagnus Damm }; 35b9dbf951SMagnus Damm 36b9dbf951SMagnus Damm #define STI_CONTROL 0x00 37b9dbf951SMagnus Damm #define STI_COMPA_H 0x10 38b9dbf951SMagnus Damm #define STI_COMPA_L 0x14 39b9dbf951SMagnus Damm #define STI_COMPB_H 0x18 40b9dbf951SMagnus Damm #define STI_COMPB_L 0x1c 41b9dbf951SMagnus Damm #define STI_COUNT_H 0x20 42b9dbf951SMagnus Damm #define STI_COUNT_L 0x24 43b9dbf951SMagnus Damm #define STI_COUNT_RAW_H 0x28 44b9dbf951SMagnus Damm #define STI_COUNT_RAW_L 0x2c 45b9dbf951SMagnus Damm #define STI_SET_H 0x30 46b9dbf951SMagnus Damm #define STI_SET_L 0x34 47b9dbf951SMagnus Damm #define STI_INTSTATUS 0x40 48b9dbf951SMagnus Damm #define STI_INTRAWSTATUS 0x44 49b9dbf951SMagnus Damm #define STI_INTENSET 0x48 50b9dbf951SMagnus Damm #define STI_INTENCLR 0x4c 51b9dbf951SMagnus Damm #define STI_INTFFCLR 0x50 52b9dbf951SMagnus Damm 53b9dbf951SMagnus Damm static inline unsigned long em_sti_read(struct em_sti_priv *p, int offs) 54b9dbf951SMagnus Damm { 55b9dbf951SMagnus Damm return ioread32(p->base + offs); 56b9dbf951SMagnus Damm } 57b9dbf951SMagnus Damm 58b9dbf951SMagnus Damm static inline void em_sti_write(struct em_sti_priv *p, int offs, 59b9dbf951SMagnus Damm unsigned long value) 60b9dbf951SMagnus Damm { 61b9dbf951SMagnus Damm iowrite32(value, p->base + offs); 62b9dbf951SMagnus Damm } 63b9dbf951SMagnus Damm 64b9dbf951SMagnus Damm static int em_sti_enable(struct em_sti_priv *p) 65b9dbf951SMagnus Damm { 66b9dbf951SMagnus Damm int ret; 67b9dbf951SMagnus Damm 68b9dbf951SMagnus Damm /* enable clock */ 693814ae09SNicolai Stange ret = clk_enable(p->clk); 70b9dbf951SMagnus Damm if (ret) { 71b9dbf951SMagnus Damm dev_err(&p->pdev->dev, "cannot enable clock\n"); 72b9dbf951SMagnus Damm return ret; 73b9dbf951SMagnus Damm } 74b9dbf951SMagnus Damm 75b9dbf951SMagnus Damm /* reset the counter */ 76b9dbf951SMagnus Damm em_sti_write(p, STI_SET_H, 0x40000000); 77b9dbf951SMagnus Damm em_sti_write(p, STI_SET_L, 0x00000000); 78b9dbf951SMagnus Damm 79b9dbf951SMagnus Damm /* mask and clear pending interrupts */ 80b9dbf951SMagnus Damm em_sti_write(p, STI_INTENCLR, 3); 81b9dbf951SMagnus Damm em_sti_write(p, STI_INTFFCLR, 3); 82b9dbf951SMagnus Damm 83b9dbf951SMagnus Damm /* enable updates of counter registers */ 84b9dbf951SMagnus Damm em_sti_write(p, STI_CONTROL, 1); 85b9dbf951SMagnus Damm 86b9dbf951SMagnus Damm return 0; 87b9dbf951SMagnus Damm } 88b9dbf951SMagnus Damm 89b9dbf951SMagnus Damm static void em_sti_disable(struct em_sti_priv *p) 90b9dbf951SMagnus Damm { 91b9dbf951SMagnus Damm /* mask interrupts */ 92b9dbf951SMagnus Damm em_sti_write(p, STI_INTENCLR, 3); 93b9dbf951SMagnus Damm 94b9dbf951SMagnus Damm /* stop clock */ 953814ae09SNicolai Stange clk_disable(p->clk); 96b9dbf951SMagnus Damm } 97b9dbf951SMagnus Damm 98a5a1d1c2SThomas Gleixner static u64 em_sti_count(struct em_sti_priv *p) 99b9dbf951SMagnus Damm { 100a5a1d1c2SThomas Gleixner u64 ticks; 101b9dbf951SMagnus Damm unsigned long flags; 102b9dbf951SMagnus Damm 103b9dbf951SMagnus Damm /* the STI hardware buffers the 48-bit count, but to 104b9dbf951SMagnus Damm * break it out into two 32-bit access the registers 105b9dbf951SMagnus Damm * must be accessed in a certain order. 106b9dbf951SMagnus Damm * Always read STI_COUNT_H before STI_COUNT_L. 107b9dbf951SMagnus Damm */ 108b9dbf951SMagnus Damm raw_spin_lock_irqsave(&p->lock, flags); 109a5a1d1c2SThomas Gleixner ticks = (u64)(em_sti_read(p, STI_COUNT_H) & 0xffff) << 32; 110b9dbf951SMagnus Damm ticks |= em_sti_read(p, STI_COUNT_L); 111b9dbf951SMagnus Damm raw_spin_unlock_irqrestore(&p->lock, flags); 112b9dbf951SMagnus Damm 113b9dbf951SMagnus Damm return ticks; 114b9dbf951SMagnus Damm } 115b9dbf951SMagnus Damm 116a5a1d1c2SThomas Gleixner static u64 em_sti_set_next(struct em_sti_priv *p, u64 next) 117b9dbf951SMagnus Damm { 118b9dbf951SMagnus Damm unsigned long flags; 119b9dbf951SMagnus Damm 120b9dbf951SMagnus Damm raw_spin_lock_irqsave(&p->lock, flags); 121b9dbf951SMagnus Damm 122b9dbf951SMagnus Damm /* mask compare A interrupt */ 123b9dbf951SMagnus Damm em_sti_write(p, STI_INTENCLR, 1); 124b9dbf951SMagnus Damm 125b9dbf951SMagnus Damm /* update compare A value */ 126b9dbf951SMagnus Damm em_sti_write(p, STI_COMPA_H, next >> 32); 127b9dbf951SMagnus Damm em_sti_write(p, STI_COMPA_L, next & 0xffffffff); 128b9dbf951SMagnus Damm 129b9dbf951SMagnus Damm /* clear compare A interrupt source */ 130b9dbf951SMagnus Damm em_sti_write(p, STI_INTFFCLR, 1); 131b9dbf951SMagnus Damm 132b9dbf951SMagnus Damm /* unmask compare A interrupt */ 133b9dbf951SMagnus Damm em_sti_write(p, STI_INTENSET, 1); 134b9dbf951SMagnus Damm 135b9dbf951SMagnus Damm raw_spin_unlock_irqrestore(&p->lock, flags); 136b9dbf951SMagnus Damm 137b9dbf951SMagnus Damm return next; 138b9dbf951SMagnus Damm } 139b9dbf951SMagnus Damm 140b9dbf951SMagnus Damm static irqreturn_t em_sti_interrupt(int irq, void *dev_id) 141b9dbf951SMagnus Damm { 142b9dbf951SMagnus Damm struct em_sti_priv *p = dev_id; 143b9dbf951SMagnus Damm 144b9dbf951SMagnus Damm p->ced.event_handler(&p->ced); 145b9dbf951SMagnus Damm return IRQ_HANDLED; 146b9dbf951SMagnus Damm } 147b9dbf951SMagnus Damm 148b9dbf951SMagnus Damm static int em_sti_start(struct em_sti_priv *p, unsigned int user) 149b9dbf951SMagnus Damm { 150b9dbf951SMagnus Damm unsigned long flags; 151b9dbf951SMagnus Damm int used_before; 152b9dbf951SMagnus Damm int ret = 0; 153b9dbf951SMagnus Damm 154b9dbf951SMagnus Damm raw_spin_lock_irqsave(&p->lock, flags); 155b9dbf951SMagnus Damm used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT]; 156b9dbf951SMagnus Damm if (!used_before) 157b9dbf951SMagnus Damm ret = em_sti_enable(p); 158b9dbf951SMagnus Damm 159b9dbf951SMagnus Damm if (!ret) 160b9dbf951SMagnus Damm p->active[user] = 1; 161b9dbf951SMagnus Damm raw_spin_unlock_irqrestore(&p->lock, flags); 162b9dbf951SMagnus Damm 163b9dbf951SMagnus Damm return ret; 164b9dbf951SMagnus Damm } 165b9dbf951SMagnus Damm 166b9dbf951SMagnus Damm static void em_sti_stop(struct em_sti_priv *p, unsigned int user) 167b9dbf951SMagnus Damm { 168b9dbf951SMagnus Damm unsigned long flags; 169b9dbf951SMagnus Damm int used_before, used_after; 170b9dbf951SMagnus Damm 171b9dbf951SMagnus Damm raw_spin_lock_irqsave(&p->lock, flags); 172b9dbf951SMagnus Damm used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT]; 173b9dbf951SMagnus Damm p->active[user] = 0; 174b9dbf951SMagnus Damm used_after = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT]; 175b9dbf951SMagnus Damm 176b9dbf951SMagnus Damm if (used_before && !used_after) 177b9dbf951SMagnus Damm em_sti_disable(p); 178b9dbf951SMagnus Damm raw_spin_unlock_irqrestore(&p->lock, flags); 179b9dbf951SMagnus Damm } 180b9dbf951SMagnus Damm 181b9dbf951SMagnus Damm static struct em_sti_priv *cs_to_em_sti(struct clocksource *cs) 182b9dbf951SMagnus Damm { 183b9dbf951SMagnus Damm return container_of(cs, struct em_sti_priv, cs); 184b9dbf951SMagnus Damm } 185b9dbf951SMagnus Damm 186a5a1d1c2SThomas Gleixner static u64 em_sti_clocksource_read(struct clocksource *cs) 187b9dbf951SMagnus Damm { 188b9dbf951SMagnus Damm return em_sti_count(cs_to_em_sti(cs)); 189b9dbf951SMagnus Damm } 190b9dbf951SMagnus Damm 191b9dbf951SMagnus Damm static int em_sti_clocksource_enable(struct clocksource *cs) 192b9dbf951SMagnus Damm { 193b9dbf951SMagnus Damm struct em_sti_priv *p = cs_to_em_sti(cs); 194b9dbf951SMagnus Damm 1954e53aa2fSNicolai Stange return em_sti_start(p, USER_CLOCKSOURCE); 196b9dbf951SMagnus Damm } 197b9dbf951SMagnus Damm 198b9dbf951SMagnus Damm static void em_sti_clocksource_disable(struct clocksource *cs) 199b9dbf951SMagnus Damm { 200b9dbf951SMagnus Damm em_sti_stop(cs_to_em_sti(cs), USER_CLOCKSOURCE); 201b9dbf951SMagnus Damm } 202b9dbf951SMagnus Damm 203b9dbf951SMagnus Damm static void em_sti_clocksource_resume(struct clocksource *cs) 204b9dbf951SMagnus Damm { 205b9dbf951SMagnus Damm em_sti_clocksource_enable(cs); 206b9dbf951SMagnus Damm } 207b9dbf951SMagnus Damm 208b9dbf951SMagnus Damm static int em_sti_register_clocksource(struct em_sti_priv *p) 209b9dbf951SMagnus Damm { 210b9dbf951SMagnus Damm struct clocksource *cs = &p->cs; 211b9dbf951SMagnus Damm 212b9dbf951SMagnus Damm cs->name = dev_name(&p->pdev->dev); 213b9dbf951SMagnus Damm cs->rating = 200; 214b9dbf951SMagnus Damm cs->read = em_sti_clocksource_read; 215b9dbf951SMagnus Damm cs->enable = em_sti_clocksource_enable; 216b9dbf951SMagnus Damm cs->disable = em_sti_clocksource_disable; 217b9dbf951SMagnus Damm cs->suspend = em_sti_clocksource_disable; 218b9dbf951SMagnus Damm cs->resume = em_sti_clocksource_resume; 219b9dbf951SMagnus Damm cs->mask = CLOCKSOURCE_MASK(48); 220b9dbf951SMagnus Damm cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 221b9dbf951SMagnus Damm 222b9dbf951SMagnus Damm dev_info(&p->pdev->dev, "used as clock source\n"); 223b9dbf951SMagnus Damm 2244e53aa2fSNicolai Stange clocksource_register_hz(cs, p->rate); 225b9dbf951SMagnus Damm return 0; 226b9dbf951SMagnus Damm } 227b9dbf951SMagnus Damm 228b9dbf951SMagnus Damm static struct em_sti_priv *ced_to_em_sti(struct clock_event_device *ced) 229b9dbf951SMagnus Damm { 230b9dbf951SMagnus Damm return container_of(ced, struct em_sti_priv, ced); 231b9dbf951SMagnus Damm } 232b9dbf951SMagnus Damm 23375f94061SViresh Kumar static int em_sti_clock_event_shutdown(struct clock_event_device *ced) 23475f94061SViresh Kumar { 23575f94061SViresh Kumar struct em_sti_priv *p = ced_to_em_sti(ced); 23675f94061SViresh Kumar em_sti_stop(p, USER_CLOCKEVENT); 23775f94061SViresh Kumar return 0; 23875f94061SViresh Kumar } 23975f94061SViresh Kumar 24075f94061SViresh Kumar static int em_sti_clock_event_set_oneshot(struct clock_event_device *ced) 241b9dbf951SMagnus Damm { 242b9dbf951SMagnus Damm struct em_sti_priv *p = ced_to_em_sti(ced); 243b9dbf951SMagnus Damm 244b9dbf951SMagnus Damm dev_info(&p->pdev->dev, "used for oneshot clock events\n"); 245b9dbf951SMagnus Damm em_sti_start(p, USER_CLOCKEVENT); 24675f94061SViresh Kumar return 0; 247b9dbf951SMagnus Damm } 248b9dbf951SMagnus Damm 249b9dbf951SMagnus Damm static int em_sti_clock_event_next(unsigned long delta, 250b9dbf951SMagnus Damm struct clock_event_device *ced) 251b9dbf951SMagnus Damm { 252b9dbf951SMagnus Damm struct em_sti_priv *p = ced_to_em_sti(ced); 253a5a1d1c2SThomas Gleixner u64 next; 254b9dbf951SMagnus Damm int safe; 255b9dbf951SMagnus Damm 256b9dbf951SMagnus Damm next = em_sti_set_next(p, em_sti_count(p) + delta); 257b9dbf951SMagnus Damm safe = em_sti_count(p) < (next - 1); 258b9dbf951SMagnus Damm 259b9dbf951SMagnus Damm return !safe; 260b9dbf951SMagnus Damm } 261b9dbf951SMagnus Damm 262b9dbf951SMagnus Damm static void em_sti_register_clockevent(struct em_sti_priv *p) 263b9dbf951SMagnus Damm { 264b9dbf951SMagnus Damm struct clock_event_device *ced = &p->ced; 265b9dbf951SMagnus Damm 266b9dbf951SMagnus Damm ced->name = dev_name(&p->pdev->dev); 267b9dbf951SMagnus Damm ced->features = CLOCK_EVT_FEAT_ONESHOT; 268b9dbf951SMagnus Damm ced->rating = 200; 2692199a557SMagnus Damm ced->cpumask = cpu_possible_mask; 270b9dbf951SMagnus Damm ced->set_next_event = em_sti_clock_event_next; 27175f94061SViresh Kumar ced->set_state_shutdown = em_sti_clock_event_shutdown; 27275f94061SViresh Kumar ced->set_state_oneshot = em_sti_clock_event_set_oneshot; 273b9dbf951SMagnus Damm 274b9dbf951SMagnus Damm dev_info(&p->pdev->dev, "used for clock events\n"); 275b9dbf951SMagnus Damm 2764e53aa2fSNicolai Stange clockevents_config_and_register(ced, p->rate, 2, 0xffffffff); 277b9dbf951SMagnus Damm } 278b9dbf951SMagnus Damm 2791850514bSGreg Kroah-Hartman static int em_sti_probe(struct platform_device *pdev) 280b9dbf951SMagnus Damm { 281b9dbf951SMagnus Damm struct em_sti_priv *p; 2821745e696SLaurent Pinchart int irq; 2833814ae09SNicolai Stange int ret; 284b9dbf951SMagnus Damm 2851745e696SLaurent Pinchart p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); 28639dd5677SJingoo Han if (p == NULL) 2871745e696SLaurent Pinchart return -ENOMEM; 288b9dbf951SMagnus Damm 289b9dbf951SMagnus Damm p->pdev = pdev; 290b9dbf951SMagnus Damm platform_set_drvdata(pdev, p); 291b9dbf951SMagnus Damm 292b9dbf951SMagnus Damm irq = platform_get_irq(pdev, 0); 2939f475d08SStephen Boyd if (irq < 0) 2945c23a558SGustavo A. R. Silva return irq; 295b9dbf951SMagnus Damm 296b9dbf951SMagnus Damm /* map memory, let base point to the STI instance */ 297*9a97b2fbSYangtao Li p->base = devm_platform_ioremap_resource(pdev, 0); 2981745e696SLaurent Pinchart if (IS_ERR(p->base)) 2991745e696SLaurent Pinchart return PTR_ERR(p->base); 300b9dbf951SMagnus Damm 3015c23a558SGustavo A. R. Silva ret = devm_request_irq(&pdev->dev, irq, em_sti_interrupt, 3023814ae09SNicolai Stange IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, 3035c23a558SGustavo A. R. Silva dev_name(&pdev->dev), p); 3045c23a558SGustavo A. R. Silva if (ret) { 3053814ae09SNicolai Stange dev_err(&pdev->dev, "failed to request low IRQ\n"); 3065c23a558SGustavo A. R. Silva return ret; 3073814ae09SNicolai Stange } 3083814ae09SNicolai Stange 309b9dbf951SMagnus Damm /* get hold of clock */ 3101745e696SLaurent Pinchart p->clk = devm_clk_get(&pdev->dev, "sclk"); 311b9dbf951SMagnus Damm if (IS_ERR(p->clk)) { 312b9dbf951SMagnus Damm dev_err(&pdev->dev, "cannot get clock\n"); 3131745e696SLaurent Pinchart return PTR_ERR(p->clk); 314b9dbf951SMagnus Damm } 315b9dbf951SMagnus Damm 3163814ae09SNicolai Stange ret = clk_prepare(p->clk); 3173814ae09SNicolai Stange if (ret < 0) { 3183814ae09SNicolai Stange dev_err(&pdev->dev, "cannot prepare clock\n"); 3193814ae09SNicolai Stange return ret; 320b9dbf951SMagnus Damm } 321b9dbf951SMagnus Damm 3224e53aa2fSNicolai Stange ret = clk_enable(p->clk); 3234e53aa2fSNicolai Stange if (ret < 0) { 3244e53aa2fSNicolai Stange dev_err(&p->pdev->dev, "cannot enable clock\n"); 3254e53aa2fSNicolai Stange clk_unprepare(p->clk); 3264e53aa2fSNicolai Stange return ret; 3274e53aa2fSNicolai Stange } 3284e53aa2fSNicolai Stange p->rate = clk_get_rate(p->clk); 3294e53aa2fSNicolai Stange clk_disable(p->clk); 3304e53aa2fSNicolai Stange 331b9dbf951SMagnus Damm raw_spin_lock_init(&p->lock); 332b9dbf951SMagnus Damm em_sti_register_clockevent(p); 333b9dbf951SMagnus Damm em_sti_register_clocksource(p); 334b9dbf951SMagnus Damm return 0; 335b9dbf951SMagnus Damm } 336b9dbf951SMagnus Damm 3371850514bSGreg Kroah-Hartman static int em_sti_remove(struct platform_device *pdev) 338b9dbf951SMagnus Damm { 339b9dbf951SMagnus Damm return -EBUSY; /* cannot unregister clockevent and clocksource */ 340b9dbf951SMagnus Damm } 341b9dbf951SMagnus Damm 3421850514bSGreg Kroah-Hartman static const struct of_device_id em_sti_dt_ids[] = { 343fc0830feSMagnus Damm { .compatible = "renesas,em-sti", }, 344fc0830feSMagnus Damm {}, 345fc0830feSMagnus Damm }; 346fc0830feSMagnus Damm MODULE_DEVICE_TABLE(of, em_sti_dt_ids); 347fc0830feSMagnus Damm 348b9dbf951SMagnus Damm static struct platform_driver em_sti_device_driver = { 349b9dbf951SMagnus Damm .probe = em_sti_probe, 3501850514bSGreg Kroah-Hartman .remove = em_sti_remove, 351b9dbf951SMagnus Damm .driver = { 352b9dbf951SMagnus Damm .name = "em_sti", 353fc0830feSMagnus Damm .of_match_table = em_sti_dt_ids, 354b9dbf951SMagnus Damm } 355b9dbf951SMagnus Damm }; 356b9dbf951SMagnus Damm 35709acc3a1SSimon Horman static int __init em_sti_init(void) 35809acc3a1SSimon Horman { 35909acc3a1SSimon Horman return platform_driver_register(&em_sti_device_driver); 36009acc3a1SSimon Horman } 36109acc3a1SSimon Horman 36209acc3a1SSimon Horman static void __exit em_sti_exit(void) 36309acc3a1SSimon Horman { 36409acc3a1SSimon Horman platform_driver_unregister(&em_sti_device_driver); 36509acc3a1SSimon Horman } 36609acc3a1SSimon Horman 36709acc3a1SSimon Horman subsys_initcall(em_sti_init); 36809acc3a1SSimon Horman module_exit(em_sti_exit); 369b9dbf951SMagnus Damm 370b9dbf951SMagnus Damm MODULE_AUTHOR("Magnus Damm"); 371b9dbf951SMagnus Damm MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver"); 372b9dbf951SMagnus Damm MODULE_LICENSE("GPL v2"); 373