linux/drivers/clocksource/sh_tmu.c
<<
>>
Prefs
   1/*
   2 * SuperH Timer Support - TMU
   3 *
   4 *  Copyright (C) 2009 Magnus Damm
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 */
  19
  20#include <linux/init.h>
  21#include <linux/platform_device.h>
  22#include <linux/spinlock.h>
  23#include <linux/interrupt.h>
  24#include <linux/ioport.h>
  25#include <linux/delay.h>
  26#include <linux/io.h>
  27#include <linux/clk.h>
  28#include <linux/irq.h>
  29#include <linux/err.h>
  30#include <linux/clocksource.h>
  31#include <linux/clockchips.h>
  32#include <linux/sh_timer.h>
  33
  34struct sh_tmu_priv {
  35        void __iomem *mapbase;
  36        struct clk *clk;
  37        struct irqaction irqaction;
  38        struct platform_device *pdev;
  39        unsigned long rate;
  40        unsigned long periodic;
  41        struct clock_event_device ced;
  42        struct clocksource cs;
  43};
  44
  45static DEFINE_SPINLOCK(sh_tmu_lock);
  46
  47#define TSTR -1 /* shared register */
  48#define TCOR  0 /* channel register */
  49#define TCNT 1 /* channel register */
  50#define TCR 2 /* channel register */
  51
  52static inline unsigned long sh_tmu_read(struct sh_tmu_priv *p, int reg_nr)
  53{
  54        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  55        void __iomem *base = p->mapbase;
  56        unsigned long offs;
  57
  58        if (reg_nr == TSTR)
  59                return ioread8(base - cfg->channel_offset);
  60
  61        offs = reg_nr << 2;
  62
  63        if (reg_nr == TCR)
  64                return ioread16(base + offs);
  65        else
  66                return ioread32(base + offs);
  67}
  68
  69static inline void sh_tmu_write(struct sh_tmu_priv *p, int reg_nr,
  70                                unsigned long value)
  71{
  72        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  73        void __iomem *base = p->mapbase;
  74        unsigned long offs;
  75
  76        if (reg_nr == TSTR) {
  77                iowrite8(value, base - cfg->channel_offset);
  78                return;
  79        }
  80
  81        offs = reg_nr << 2;
  82
  83        if (reg_nr == TCR)
  84                iowrite16(value, base + offs);
  85        else
  86                iowrite32(value, base + offs);
  87}
  88
  89static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start)
  90{
  91        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  92        unsigned long flags, value;
  93
  94        /* start stop register shared by multiple timer channels */
  95        spin_lock_irqsave(&sh_tmu_lock, flags);
  96        value = sh_tmu_read(p, TSTR);
  97
  98        if (start)
  99                value |= 1 << cfg->timer_bit;
 100        else
 101                value &= ~(1 << cfg->timer_bit);
 102
 103        sh_tmu_write(p, TSTR, value);
 104        spin_unlock_irqrestore(&sh_tmu_lock, flags);
 105}
 106
 107static int sh_tmu_enable(struct sh_tmu_priv *p)
 108{
 109        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
 110        int ret;
 111
 112        /* enable clock */
 113        ret = clk_enable(p->clk);
 114        if (ret) {
 115                pr_err("sh_tmu: cannot enable clock \"%s\"\n", cfg->clk);
 116                return ret;
 117        }
 118
 119        /* make sure channel is disabled */
 120        sh_tmu_start_stop_ch(p, 0);
 121
 122        /* maximum timeout */
 123        sh_tmu_write(p, TCOR, 0xffffffff);
 124        sh_tmu_write(p, TCNT, 0xffffffff);
 125
 126        /* configure channel to parent clock / 4, irq off */
 127        p->rate = clk_get_rate(p->clk) / 4;
 128        sh_tmu_write(p, TCR, 0x0000);
 129
 130        /* enable channel */
 131        sh_tmu_start_stop_ch(p, 1);
 132
 133        return 0;
 134}
 135
 136static void sh_tmu_disable(struct sh_tmu_priv *p)
 137{
 138        /* disable channel */
 139        sh_tmu_start_stop_ch(p, 0);
 140
 141        /* disable interrupts in TMU block */
 142        sh_tmu_write(p, TCR, 0x0000);
 143
 144        /* stop clock */
 145        clk_disable(p->clk);
 146}
 147
 148static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta,
 149                            int periodic)
 150{
 151        /* stop timer */
 152        sh_tmu_start_stop_ch(p, 0);
 153
 154        /* acknowledge interrupt */
 155        sh_tmu_read(p, TCR);
 156
 157        /* enable interrupt */
 158        sh_tmu_write(p, TCR, 0x0020);
 159
 160        /* reload delta value in case of periodic timer */
 161        if (periodic)
 162                sh_tmu_write(p, TCOR, delta);
 163        else
 164                sh_tmu_write(p, TCOR, 0xffffffff);
 165
 166        sh_tmu_write(p, TCNT, delta);
 167
 168        /* start timer */
 169        sh_tmu_start_stop_ch(p, 1);
 170}
 171
 172static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
 173{
 174        struct sh_tmu_priv *p = dev_id;
 175
 176        /* disable or acknowledge interrupt */
 177        if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT)
 178                sh_tmu_write(p, TCR, 0x0000);
 179        else
 180                sh_tmu_write(p, TCR, 0x0020);
 181
 182        /* notify clockevent layer */
 183        p->ced.event_handler(&p->ced);
 184        return IRQ_HANDLED;
 185}
 186
 187static struct sh_tmu_priv *cs_to_sh_tmu(struct clocksource *cs)
 188{
 189        return container_of(cs, struct sh_tmu_priv, cs);
 190}
 191
 192static cycle_t sh_tmu_clocksource_read(struct clocksource *cs)
 193{
 194        struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
 195
 196        return sh_tmu_read(p, TCNT) ^ 0xffffffff;
 197}
 198
 199static int sh_tmu_clocksource_enable(struct clocksource *cs)
 200{
 201        struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
 202        int ret;
 203
 204        ret = sh_tmu_enable(p);
 205        if (ret)
 206                return ret;
 207
 208        /* TODO: calculate good shift from rate and counter bit width */
 209        cs->shift = 10;
 210        cs->mult = clocksource_hz2mult(p->rate, cs->shift);
 211        return 0;
 212}
 213
 214static void sh_tmu_clocksource_disable(struct clocksource *cs)
 215{
 216        sh_tmu_disable(cs_to_sh_tmu(cs));
 217}
 218
 219static int sh_tmu_register_clocksource(struct sh_tmu_priv *p,
 220                                       char *name, unsigned long rating)
 221{
 222        struct clocksource *cs = &p->cs;
 223
 224        cs->name = name;
 225        cs->rating = rating;
 226        cs->read = sh_tmu_clocksource_read;
 227        cs->enable = sh_tmu_clocksource_enable;
 228        cs->disable = sh_tmu_clocksource_disable;
 229        cs->mask = CLOCKSOURCE_MASK(32);
 230        cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
 231        pr_info("sh_tmu: %s used as clock source\n", cs->name);
 232        clocksource_register(cs);
 233        return 0;
 234}
 235
 236static struct sh_tmu_priv *ced_to_sh_tmu(struct clock_event_device *ced)
 237{
 238        return container_of(ced, struct sh_tmu_priv, ced);
 239}
 240
 241static void sh_tmu_clock_event_start(struct sh_tmu_priv *p, int periodic)
 242{
 243        struct clock_event_device *ced = &p->ced;
 244
 245        sh_tmu_enable(p);
 246
 247        /* TODO: calculate good shift from rate and counter bit width */
 248
 249        ced->shift = 32;
 250        ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
 251        ced->max_delta_ns = clockevent_delta2ns(0xffffffff, ced);
 252        ced->min_delta_ns = 5000;
 253
 254        if (periodic) {
 255                p->periodic = (p->rate + HZ/2) / HZ;
 256                sh_tmu_set_next(p, p->periodic, 1);
 257        }
 258}
 259
 260static void sh_tmu_clock_event_mode(enum clock_event_mode mode,
 261                                    struct clock_event_device *ced)
 262{
 263        struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
 264        int disabled = 0;
 265
 266        /* deal with old setting first */
 267        switch (ced->mode) {
 268        case CLOCK_EVT_MODE_PERIODIC:
 269        case CLOCK_EVT_MODE_ONESHOT:
 270                sh_tmu_disable(p);
 271                disabled = 1;
 272                break;
 273        default:
 274                break;
 275        }
 276
 277        switch (mode) {
 278        case CLOCK_EVT_MODE_PERIODIC:
 279                pr_info("sh_tmu: %s used for periodic clock events\n",
 280                        ced->name);
 281                sh_tmu_clock_event_start(p, 1);
 282                break;
 283        case CLOCK_EVT_MODE_ONESHOT:
 284                pr_info("sh_tmu: %s used for oneshot clock events\n",
 285                        ced->name);
 286                sh_tmu_clock_event_start(p, 0);
 287                break;
 288        case CLOCK_EVT_MODE_UNUSED:
 289                if (!disabled)
 290                        sh_tmu_disable(p);
 291                break;
 292        case CLOCK_EVT_MODE_SHUTDOWN:
 293        default:
 294                break;
 295        }
 296}
 297
 298static int sh_tmu_clock_event_next(unsigned long delta,
 299                                   struct clock_event_device *ced)
 300{
 301        struct sh_tmu_priv *p = ced_to_sh_tmu(ced);
 302
 303        BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
 304
 305        /* program new delta value */
 306        sh_tmu_set_next(p, delta, 0);
 307        return 0;
 308}
 309
 310static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
 311                                       char *name, unsigned long rating)
 312{
 313        struct clock_event_device *ced = &p->ced;
 314        int ret;
 315
 316        memset(ced, 0, sizeof(*ced));
 317
 318        ced->name = name;
 319        ced->features = CLOCK_EVT_FEAT_PERIODIC;
 320        ced->features |= CLOCK_EVT_FEAT_ONESHOT;
 321        ced->rating = rating;
 322        ced->cpumask = cpumask_of(0);
 323        ced->set_next_event = sh_tmu_clock_event_next;
 324        ced->set_mode = sh_tmu_clock_event_mode;
 325
 326        ret = setup_irq(p->irqaction.irq, &p->irqaction);
 327        if (ret) {
 328                pr_err("sh_tmu: failed to request irq %d\n",
 329                       p->irqaction.irq);
 330                return;
 331        }
 332
 333        pr_info("sh_tmu: %s used for clock events\n", ced->name);
 334        clockevents_register_device(ced);
 335}
 336
 337static int sh_tmu_register(struct sh_tmu_priv *p, char *name,
 338                    unsigned long clockevent_rating,
 339                    unsigned long clocksource_rating)
 340{
 341        if (clockevent_rating)
 342                sh_tmu_register_clockevent(p, name, clockevent_rating);
 343        else if (clocksource_rating)
 344                sh_tmu_register_clocksource(p, name, clocksource_rating);
 345
 346        return 0;
 347}
 348
 349static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev)
 350{
 351        struct sh_timer_config *cfg = pdev->dev.platform_data;
 352        struct resource *res;
 353        int irq, ret;
 354        ret = -ENXIO;
 355
 356        memset(p, 0, sizeof(*p));
 357        p->pdev = pdev;
 358
 359        if (!cfg) {
 360                dev_err(&p->pdev->dev, "missing platform data\n");
 361                goto err0;
 362        }
 363
 364        platform_set_drvdata(pdev, p);
 365
 366        res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
 367        if (!res) {
 368                dev_err(&p->pdev->dev, "failed to get I/O memory\n");
 369                goto err0;
 370        }
 371
 372        irq = platform_get_irq(p->pdev, 0);
 373        if (irq < 0) {
 374                dev_err(&p->pdev->dev, "failed to get irq\n");
 375                goto err0;
 376        }
 377
 378        /* map memory, let mapbase point to our channel */
 379        p->mapbase = ioremap_nocache(res->start, resource_size(res));
 380        if (p->mapbase == NULL) {
 381                pr_err("sh_tmu: failed to remap I/O memory\n");
 382                goto err0;
 383        }
 384
 385        /* setup data for setup_irq() (too early for request_irq()) */
 386        p->irqaction.name = cfg->name;
 387        p->irqaction.handler = sh_tmu_interrupt;
 388        p->irqaction.dev_id = p;
 389        p->irqaction.irq = irq;
 390        p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
 391
 392        /* get hold of clock */
 393        p->clk = clk_get(&p->pdev->dev, cfg->clk);
 394        if (IS_ERR(p->clk)) {
 395                pr_err("sh_tmu: cannot get clock \"%s\"\n", cfg->clk);
 396                ret = PTR_ERR(p->clk);
 397                goto err1;
 398        }
 399
 400        return sh_tmu_register(p, cfg->name,
 401                               cfg->clockevent_rating,
 402                               cfg->clocksource_rating);
 403 err1:
 404        iounmap(p->mapbase);
 405 err0:
 406        return ret;
 407}
 408
 409static int __devinit sh_tmu_probe(struct platform_device *pdev)
 410{
 411        struct sh_tmu_priv *p = platform_get_drvdata(pdev);
 412        struct sh_timer_config *cfg = pdev->dev.platform_data;
 413        int ret;
 414
 415        if (p) {
 416                pr_info("sh_tmu: %s kept as earlytimer\n", cfg->name);
 417                return 0;
 418        }
 419
 420        p = kmalloc(sizeof(*p), GFP_KERNEL);
 421        if (p == NULL) {
 422                dev_err(&pdev->dev, "failed to allocate driver data\n");
 423                return -ENOMEM;
 424        }
 425
 426        ret = sh_tmu_setup(p, pdev);
 427        if (ret) {
 428                kfree(p);
 429                platform_set_drvdata(pdev, NULL);
 430        }
 431        return ret;
 432}
 433
 434static int __devexit sh_tmu_remove(struct platform_device *pdev)
 435{
 436        return -EBUSY; /* cannot unregister clockevent and clocksource */
 437}
 438
 439static struct platform_driver sh_tmu_device_driver = {
 440        .probe          = sh_tmu_probe,
 441        .remove         = __devexit_p(sh_tmu_remove),
 442        .driver         = {
 443                .name   = "sh_tmu",
 444        }
 445};
 446
 447static int __init sh_tmu_init(void)
 448{
 449        return platform_driver_register(&sh_tmu_device_driver);
 450}
 451
 452static void __exit sh_tmu_exit(void)
 453{
 454        platform_driver_unregister(&sh_tmu_device_driver);
 455}
 456
 457early_platform_init("earlytimer", &sh_tmu_device_driver);
 458module_init(sh_tmu_init);
 459module_exit(sh_tmu_exit);
 460
 461MODULE_AUTHOR("Magnus Damm");
 462MODULE_DESCRIPTION("SuperH TMU Timer Driver");
 463MODULE_LICENSE("GPL v2");
 464
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.