linux/drivers/clocksource/sh_mtu2.c
<<
>>
Prefs
   1/*
   2 * SuperH Timer Support - MTU2
   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/clockchips.h>
  31#include <linux/sh_timer.h>
  32#include <linux/slab.h>
  33#include <linux/module.h>
  34#include <linux/pm_domain.h>
  35#include <linux/pm_runtime.h>
  36
  37struct sh_mtu2_priv {
  38        void __iomem *mapbase;
  39        struct clk *clk;
  40        struct irqaction irqaction;
  41        struct platform_device *pdev;
  42        unsigned long rate;
  43        unsigned long periodic;
  44        struct clock_event_device ced;
  45};
  46
  47static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
  48
  49#define TSTR -1 /* shared register */
  50#define TCR  0 /* channel register */
  51#define TMDR 1 /* channel register */
  52#define TIOR 2 /* channel register */
  53#define TIER 3 /* channel register */
  54#define TSR  4 /* channel register */
  55#define TCNT 5 /* channel register */
  56#define TGR  6 /* channel register */
  57
  58static unsigned long mtu2_reg_offs[] = {
  59        [TCR] = 0,
  60        [TMDR] = 1,
  61        [TIOR] = 2,
  62        [TIER] = 4,
  63        [TSR] = 5,
  64        [TCNT] = 6,
  65        [TGR] = 8,
  66};
  67
  68static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr)
  69{
  70        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  71        void __iomem *base = p->mapbase;
  72        unsigned long offs;
  73
  74        if (reg_nr == TSTR)
  75                return ioread8(base + cfg->channel_offset);
  76
  77        offs = mtu2_reg_offs[reg_nr];
  78
  79        if ((reg_nr == TCNT) || (reg_nr == TGR))
  80                return ioread16(base + offs);
  81        else
  82                return ioread8(base + offs);
  83}
  84
  85static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr,
  86                                unsigned long value)
  87{
  88        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  89        void __iomem *base = p->mapbase;
  90        unsigned long offs;
  91
  92        if (reg_nr == TSTR) {
  93                iowrite8(value, base + cfg->channel_offset);
  94                return;
  95        }
  96
  97        offs = mtu2_reg_offs[reg_nr];
  98
  99        if ((reg_nr == TCNT) || (reg_nr == TGR))
 100                iowrite16(value, base + offs);
 101        else
 102                iowrite8(value, base + offs);
 103}
 104
 105static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start)
 106{
 107        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
 108        unsigned long flags, value;
 109
 110        /* start stop register shared by multiple timer channels */
 111        raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
 112        value = sh_mtu2_read(p, TSTR);
 113
 114        if (start)
 115                value |= 1 << cfg->timer_bit;
 116        else
 117                value &= ~(1 << cfg->timer_bit);
 118
 119        sh_mtu2_write(p, TSTR, value);
 120        raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
 121}
 122
 123static int sh_mtu2_enable(struct sh_mtu2_priv *p)
 124{
 125        int ret;
 126
 127        pm_runtime_get_sync(&p->pdev->dev);
 128        dev_pm_syscore_device(&p->pdev->dev, true);
 129
 130        /* enable clock */
 131        ret = clk_enable(p->clk);
 132        if (ret) {
 133                dev_err(&p->pdev->dev, "cannot enable clock\n");
 134                return ret;
 135        }
 136
 137        /* make sure channel is disabled */
 138        sh_mtu2_start_stop_ch(p, 0);
 139
 140        p->rate = clk_get_rate(p->clk) / 64;
 141        p->periodic = (p->rate + HZ/2) / HZ;
 142
 143        /* "Periodic Counter Operation" */
 144        sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */
 145        sh_mtu2_write(p, TIOR, 0);
 146        sh_mtu2_write(p, TGR, p->periodic);
 147        sh_mtu2_write(p, TCNT, 0);
 148        sh_mtu2_write(p, TMDR, 0);
 149        sh_mtu2_write(p, TIER, 0x01);
 150
 151        /* enable channel */
 152        sh_mtu2_start_stop_ch(p, 1);
 153
 154        return 0;
 155}
 156
 157static void sh_mtu2_disable(struct sh_mtu2_priv *p)
 158{
 159        /* disable channel */
 160        sh_mtu2_start_stop_ch(p, 0);
 161
 162        /* stop clock */
 163        clk_disable(p->clk);
 164
 165        dev_pm_syscore_device(&p->pdev->dev, false);
 166        pm_runtime_put(&p->pdev->dev);
 167}
 168
 169static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
 170{
 171        struct sh_mtu2_priv *p = dev_id;
 172
 173        /* acknowledge interrupt */
 174        sh_mtu2_read(p, TSR);
 175        sh_mtu2_write(p, TSR, 0xfe);
 176
 177        /* notify clockevent layer */
 178        p->ced.event_handler(&p->ced);
 179        return IRQ_HANDLED;
 180}
 181
 182static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced)
 183{
 184        return container_of(ced, struct sh_mtu2_priv, ced);
 185}
 186
 187static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
 188                                    struct clock_event_device *ced)
 189{
 190        struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced);
 191        int disabled = 0;
 192
 193        /* deal with old setting first */
 194        switch (ced->mode) {
 195        case CLOCK_EVT_MODE_PERIODIC:
 196                sh_mtu2_disable(p);
 197                disabled = 1;
 198                break;
 199        default:
 200                break;
 201        }
 202
 203        switch (mode) {
 204        case CLOCK_EVT_MODE_PERIODIC:
 205                dev_info(&p->pdev->dev, "used for periodic clock events\n");
 206                sh_mtu2_enable(p);
 207                break;
 208        case CLOCK_EVT_MODE_UNUSED:
 209                if (!disabled)
 210                        sh_mtu2_disable(p);
 211                break;
 212        case CLOCK_EVT_MODE_SHUTDOWN:
 213        default:
 214                break;
 215        }
 216}
 217
 218static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced)
 219{
 220        pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->pdev->dev);
 221}
 222
 223static void sh_mtu2_clock_event_resume(struct clock_event_device *ced)
 224{
 225        pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->pdev->dev);
 226}
 227
 228static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
 229                                       char *name, unsigned long rating)
 230{
 231        struct clock_event_device *ced = &p->ced;
 232        int ret;
 233
 234        memset(ced, 0, sizeof(*ced));
 235
 236        ced->name = name;
 237        ced->features = CLOCK_EVT_FEAT_PERIODIC;
 238        ced->rating = rating;
 239        ced->cpumask = cpumask_of(0);
 240        ced->set_mode = sh_mtu2_clock_event_mode;
 241        ced->suspend = sh_mtu2_clock_event_suspend;
 242        ced->resume = sh_mtu2_clock_event_resume;
 243
 244        dev_info(&p->pdev->dev, "used for clock events\n");
 245        clockevents_register_device(ced);
 246
 247        ret = setup_irq(p->irqaction.irq, &p->irqaction);
 248        if (ret) {
 249                dev_err(&p->pdev->dev, "failed to request irq %d\n",
 250                        p->irqaction.irq);
 251                return;
 252        }
 253}
 254
 255static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
 256                            unsigned long clockevent_rating)
 257{
 258        if (clockevent_rating)
 259                sh_mtu2_register_clockevent(p, name, clockevent_rating);
 260
 261        return 0;
 262}
 263
 264static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev)
 265{
 266        struct sh_timer_config *cfg = pdev->dev.platform_data;
 267        struct resource *res;
 268        int irq, ret;
 269        ret = -ENXIO;
 270
 271        memset(p, 0, sizeof(*p));
 272        p->pdev = pdev;
 273
 274        if (!cfg) {
 275                dev_err(&p->pdev->dev, "missing platform data\n");
 276                goto err0;
 277        }
 278
 279        platform_set_drvdata(pdev, p);
 280
 281        res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
 282        if (!res) {
 283                dev_err(&p->pdev->dev, "failed to get I/O memory\n");
 284                goto err0;
 285        }
 286
 287        irq = platform_get_irq(p->pdev, 0);
 288        if (irq < 0) {
 289                dev_err(&p->pdev->dev, "failed to get irq\n");
 290                goto err0;
 291        }
 292
 293        /* map memory, let mapbase point to our channel */
 294        p->mapbase = ioremap_nocache(res->start, resource_size(res));
 295        if (p->mapbase == NULL) {
 296                dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
 297                goto err0;
 298        }
 299
 300        /* setup data for setup_irq() (too early for request_irq()) */
 301        p->irqaction.name = dev_name(&p->pdev->dev);
 302        p->irqaction.handler = sh_mtu2_interrupt;
 303        p->irqaction.dev_id = p;
 304        p->irqaction.irq = irq;
 305        p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \
 306                             IRQF_IRQPOLL  | IRQF_NOBALANCING;
 307
 308        /* get hold of clock */
 309        p->clk = clk_get(&p->pdev->dev, "mtu2_fck");
 310        if (IS_ERR(p->clk)) {
 311                dev_err(&p->pdev->dev, "cannot get clock\n");
 312                ret = PTR_ERR(p->clk);
 313                goto err1;
 314        }
 315
 316        return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev),
 317                                cfg->clockevent_rating);
 318 err1:
 319        iounmap(p->mapbase);
 320 err0:
 321        return ret;
 322}
 323
 324static int sh_mtu2_probe(struct platform_device *pdev)
 325{
 326        struct sh_mtu2_priv *p = platform_get_drvdata(pdev);
 327        struct sh_timer_config *cfg = pdev->dev.platform_data;
 328        int ret;
 329
 330        if (!is_early_platform_device(pdev)) {
 331                pm_runtime_set_active(&pdev->dev);
 332                pm_runtime_enable(&pdev->dev);
 333        }
 334
 335        if (p) {
 336                dev_info(&pdev->dev, "kept as earlytimer\n");
 337                goto out;
 338        }
 339
 340        p = kmalloc(sizeof(*p), GFP_KERNEL);
 341        if (p == NULL) {
 342                dev_err(&pdev->dev, "failed to allocate driver data\n");
 343                return -ENOMEM;
 344        }
 345
 346        ret = sh_mtu2_setup(p, pdev);
 347        if (ret) {
 348                kfree(p);
 349                platform_set_drvdata(pdev, NULL);
 350                pm_runtime_idle(&pdev->dev);
 351                return ret;
 352        }
 353        if (is_early_platform_device(pdev))
 354                return 0;
 355
 356 out:
 357        if (cfg->clockevent_rating)
 358                pm_runtime_irq_safe(&pdev->dev);
 359        else
 360                pm_runtime_idle(&pdev->dev);
 361
 362        return 0;
 363}
 364
 365static int sh_mtu2_remove(struct platform_device *pdev)
 366{
 367        return -EBUSY; /* cannot unregister clockevent */
 368}
 369
 370static struct platform_driver sh_mtu2_device_driver = {
 371        .probe          = sh_mtu2_probe,
 372        .remove         = sh_mtu2_remove,
 373        .driver         = {
 374                .name   = "sh_mtu2",
 375        }
 376};
 377
 378static int __init sh_mtu2_init(void)
 379{
 380        return platform_driver_register(&sh_mtu2_device_driver);
 381}
 382
 383static void __exit sh_mtu2_exit(void)
 384{
 385        platform_driver_unregister(&sh_mtu2_device_driver);
 386}
 387
 388early_platform_init("earlytimer", &sh_mtu2_device_driver);
 389subsys_initcall(sh_mtu2_init);
 390module_exit(sh_mtu2_exit);
 391
 392MODULE_AUTHOR("Magnus Damm");
 393MODULE_DESCRIPTION("SuperH MTU2 Timer Driver");
 394MODULE_LICENSE("GPL v2");
 395
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.