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
  36struct sh_mtu2_priv {
  37        void __iomem *mapbase;
  38        struct clk *clk;
  39        struct irqaction irqaction;
  40        struct platform_device *pdev;
  41        unsigned long rate;
  42        unsigned long periodic;
  43        struct clock_event_device ced;
  44};
  45
  46static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
  47
  48#define TSTR -1 /* shared register */
  49#define TCR  0 /* channel register */
  50#define TMDR 1 /* channel register */
  51#define TIOR 2 /* channel register */
  52#define TIER 3 /* channel register */
  53#define TSR  4 /* channel register */
  54#define TCNT 5 /* channel register */
  55#define TGR  6 /* channel register */
  56
  57static unsigned long mtu2_reg_offs[] = {
  58        [TCR] = 0,
  59        [TMDR] = 1,
  60        [TIOR] = 2,
  61        [TIER] = 4,
  62        [TSR] = 5,
  63        [TCNT] = 6,
  64        [TGR] = 8,
  65};
  66
  67static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr)
  68{
  69        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  70        void __iomem *base = p->mapbase;
  71        unsigned long offs;
  72
  73        if (reg_nr == TSTR)
  74                return ioread8(base + cfg->channel_offset);
  75
  76        offs = mtu2_reg_offs[reg_nr];
  77
  78        if ((reg_nr == TCNT) || (reg_nr == TGR))
  79                return ioread16(base + offs);
  80        else
  81                return ioread8(base + offs);
  82}
  83
  84static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr,
  85                                unsigned long value)
  86{
  87        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
  88        void __iomem *base = p->mapbase;
  89        unsigned long offs;
  90
  91        if (reg_nr == TSTR) {
  92                iowrite8(value, base + cfg->channel_offset);
  93                return;
  94        }
  95
  96        offs = mtu2_reg_offs[reg_nr];
  97
  98        if ((reg_nr == TCNT) || (reg_nr == TGR))
  99                iowrite16(value, base + offs);
 100        else
 101                iowrite8(value, base + offs);
 102}
 103
 104static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start)
 105{
 106        struct sh_timer_config *cfg = p->pdev->dev.platform_data;
 107        unsigned long flags, value;
 108
 109        /* start stop register shared by multiple timer channels */
 110        raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
 111        value = sh_mtu2_read(p, TSTR);
 112
 113        if (start)
 114                value |= 1 << cfg->timer_bit;
 115        else
 116                value &= ~(1 << cfg->timer_bit);
 117
 118        sh_mtu2_write(p, TSTR, value);
 119        raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
 120}
 121
 122static int sh_mtu2_enable(struct sh_mtu2_priv *p)
 123{
 124        int ret;
 125
 126        /* enable clock */
 127        ret = clk_enable(p->clk);
 128        if (ret) {
 129                dev_err(&p->pdev->dev, "cannot enable clock\n");
 130                return ret;
 131        }
 132
 133        /* make sure channel is disabled */
 134        sh_mtu2_start_stop_ch(p, 0);
 135
 136        p->rate = clk_get_rate(p->clk) / 64;
 137        p->periodic = (p->rate + HZ/2) / HZ;
 138
 139        /* "Periodic Counter Operation" */
 140        sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */
 141        sh_mtu2_write(p, TIOR, 0);
 142        sh_mtu2_write(p, TGR, p->periodic);
 143        sh_mtu2_write(p, TCNT, 0);
 144        sh_mtu2_write(p, TMDR, 0);
 145        sh_mtu2_write(p, TIER, 0x01);
 146
 147        /* enable channel */
 148        sh_mtu2_start_stop_ch(p, 1);
 149
 150        return 0;
 151}
 152
 153static void sh_mtu2_disable(struct sh_mtu2_priv *p)
 154{
 155        /* disable channel */
 156        sh_mtu2_start_stop_ch(p, 0);
 157
 158        /* stop clock */
 159        clk_disable(p->clk);
 160}
 161
 162static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
 163{
 164        struct sh_mtu2_priv *p = dev_id;
 165
 166        /* acknowledge interrupt */
 167        sh_mtu2_read(p, TSR);
 168        sh_mtu2_write(p, TSR, 0xfe);
 169
 170        /* notify clockevent layer */
 171        p->ced.event_handler(&p->ced);
 172        return IRQ_HANDLED;
 173}
 174
 175static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced)
 176{
 177        return container_of(ced, struct sh_mtu2_priv, ced);
 178}
 179
 180static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
 181                                    struct clock_event_device *ced)
 182{
 183        struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced);
 184        int disabled = 0;
 185
 186        /* deal with old setting first */
 187        switch (ced->mode) {
 188        case CLOCK_EVT_MODE_PERIODIC:
 189                sh_mtu2_disable(p);
 190                disabled = 1;
 191                break;
 192        default:
 193                break;
 194        }
 195
 196        switch (mode) {
 197        case CLOCK_EVT_MODE_PERIODIC:
 198                dev_info(&p->pdev->dev, "used for periodic clock events\n");
 199                sh_mtu2_enable(p);
 200                break;
 201        case CLOCK_EVT_MODE_UNUSED:
 202                if (!disabled)
 203                        sh_mtu2_disable(p);
 204                break;
 205        case CLOCK_EVT_MODE_SHUTDOWN:
 206        default:
 207                break;
 208        }
 209}
 210
 211static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
 212                                       char *name, unsigned long rating)
 213{
 214        struct clock_event_device *ced = &p->ced;
 215        int ret;
 216
 217        memset(ced, 0, sizeof(*ced));
 218
 219        ced->name = name;
 220        ced->features = CLOCK_EVT_FEAT_PERIODIC;
 221        ced->rating = rating;
 222        ced->cpumask = cpumask_of(0);
 223        ced->set_mode = sh_mtu2_clock_event_mode;
 224
 225        dev_info(&p->pdev->dev, "used for clock events\n");
 226        clockevents_register_device(ced);
 227
 228        ret = setup_irq(p->irqaction.irq, &p->irqaction);
 229        if (ret) {
 230                dev_err(&p->pdev->dev, "failed to requesvss="sref">name 157
name, > 1322ced)
p->irqaction);
/* m2ke su23="sref">rating)
  952/clocksou2ce/sh_mtu2.c#L134" id="L234" c2ass="line" namid="L211" class="line" name="L211"> 211ref="+cod2=p" class="sref">p, 2);
 211rref">pp2/a>->rate<2a> = /a> * s="sref">p,
 212                                       char *rate<2a        213{
 214/* &2uot;P23g" class="sref">sh_timer_config * 230                 214p, pdev->p,
  952ass="sref2>p, irqreturn_t p,  186p,  175p, dev_info(&p, /a> *s="sref">p,
ce/sh_mtu2.c#L212" id="L212" class="line" name="L212"> 212                                       char * 214 = /* e2able 24f" class="sref">container_of(p->pdev->dev. 107        unsigned long flags, p, 2);
p->flags,  230 c#L163" id="L163" class="line" name="L163"> 1ef="drivers/clocksource/sh_mtu2.c#L217" id="L217" class="line" name="L217"> 217e="L151">2151}
=irqaction" class="sref">irq-" class="sref">ENXIOqaction" class=ENXIO17" id="L217" class="line" name="L217"> 217ess="sref2"drivers/clocksource/sh_2tu2.c25sref">irqreturn_t cpumask_of(="drivers/clocksource/sh_mtu2.c#L218" id="L218"cksource/sh_mtu2.c#L158"18
  952ef="+code2sh_mtu2_priv" class="sre2">sh_25ock_event_mode" class="sref""failed to requesvss="sref">name  952ess="sref2span class="comment">/* 2isabl25ef">dev_info(& 156<> 203           class="sref">dev.dev_err(&p, 2);
dev, "failed to requesvss="sref">name 157
clockevents_register_dev        <2pan class="comment">/* s2op cl25rivers/clocksource/shgotode" class="sref"v
  952eef="+cod2id="L159" class="line" n2me="L25_mtu2.c#L210" id="L210" class="line" name="L210"> 210p<2a>->sh_mtu2_clock_event_mo2>
="+code=fc#L2drvsigned long clockevents_register_devstatic irqreturn_t sh_mtu2_inte2rupt<26class="sref">cpumask_of(ksource/sh_mtu2.c# id="L2ref="+code=platform"+code=fu2.c#e href=f="+code=dev_erm"+code=fu2.c#e href=/a>, &p->clockevents_register_devsf="+code2truct  114  3           ksource/sh_mtu2.c# id="L2s="sref">dev_err(&p = dev, "failed to requesvss="sref">name 157
clockevents_register_dev        <2pan class="comment">/* a2knowl265ivers/clocksource/shgotode" class="sref"v
  952urce/sh_m2u2.c#L167" id="L167" cla2s="li2e" name="L167" id="L210" class="line" name="L210"> 2102, TSR, 0xfe);
ref="+code=platform"+code=fu2.cclass="line" name=m"+code=fu2.ccla/a>, &p->clockevents_register_devssref">p<2pan class="comment">/* n2tify 26230"> 230                r_mtu20s="sref">dev_err(&pdev->dev, "failed to requesvss="sref">name 157
clockevents_register_dev.ced
  952/clocksou2ce/sh_mtu2.c#L173" id="L273" c2ass="line" nam id="L210" class="line" name="L210"> 210
sh_mtu2_start_stop_ch2static st2uct TMDR="L134"> 134        sh_mtu2_start_stop_ch(ced_t2_sh_m27="sref">pdev->nameref="+code=platforiore/ap_nocach2ers/clocksourceiore/ap_nocach2/a>, &;
f="+code=dev_err" href=_/a>, &  952>        2eturn  230                namereef="+code=platforNULLers/clocksourceNULL="L2s="sref">dev_err(&ced, struct 2a hre27rivers/clocksource/sh>->dev, "failed to requesvss="sref">name 157
clockevents_register_dev>
pdev
  952/sref">p<2d  210(enu2 sh_mtu2_register_clo">clock_e2ent_device * lagsef="+ref">irq<() (too earlyef="+class="irq<())stop_ch" class="sref">sh_mtu2_start_stop_ch(        2truct cpumask_of(code=p" class="sref">p->irqaction);
 220        deline" name="L220"> ">delinea>, "failed to requesvss="sref">name 157
clockevents_register_dev=p" class2"sref">p = nameirqaction);
clockevents_register_dev=tatic st2_mtu2.c#L185" id="L185" 2lass=2line" name="L1e" class="sref""failed to requesvss="sref">nameirqaction);
clockevents_register_dev=ed_to_sh2pan class="comment">/* d2al wi28="sref">pdev->nameirqaction);
clockevents_register_dev=        2/sh_mtu2.c#L187" id="L182" cla28ed);
nameirqaction);
clockevents_register_dev=ed" clas2) {
clockevents_register_dev=
na"+code=p"2class="sref">p);
 140sh_mtu2_start_stop_ch(=quot;failed to requesvss="sref">name) / 64;
, "failed to requesvss="sref">name 157
clockevents_register_dev" name="L292"> 192        defa2lt:
<2 href="drivers                , &p->dev_err(& 293                br2ak;
<29="sref">rating)
<>->dev, "failed to requesvss="sref">name 157
clockevents_register_dev" name="L294"> 194        }
irqaction., &p->clockevents_register_dev"tatic st2clockevents_register_dev        s2itch (pdev< id="L210" class="line" name="L210"> 210memset(c:
sss="sref"L227"> s="sref">p,
, (urce/s)    deline" name="L220"> ">delinea>, "failed to requesvss="sref">name 157
rate<2 class="s2ef">p->pdevdev.->  952code=p" c2ass="sref">p);
 201        case3iounma"failed to iounma&/a>, &p-> L95" id="L95" class="line" name="L95">  953:

                br303" c30ss="line" name="L173"> 173}
 217p);
<30175" id="L175" class="line" name="L175"> 175 205        case3dev_info(&:
/a> *  oba href="drivers/clocksou  oba/a>, _mtu2.c#L212" id="L212="+code=fl27" id="L227" class=="+code=fl27" i                      u2.c#L157" id="L157" class id="L214" class="line" name="L214"> 214 307                br3ak;
<30f" class="sref">container_of( 208        }
p-> 212                                       char *  953:8name="L30f">p-> 217
sh_mtu2_clock_event_mo3static vo3d  214(struct ced&u2.c#L157" id="L157" class="line" name="L157"> 157
ss=id="L227" class=ss=ilass L95" id="L95" class="line" name="L95">  953unsigned 3ong sh_mtu2_disable(struct         3truct  114                               char *dev_err(&ced3/a> =31="+code=CLOCK_EVT_MODe" class="sref"+codode=dev" class="sref">dev, &u2.c#L157" id="L157" class="line" name="L157"> 157
clockevents_register_de32.c#L216"3id="L216" class="line" n3me="L315ivers/clocksource/she="L173 id="L186" class="line" name="L186"> 186 210, 03 sizeof(* = TSR, 0xfe);
GFP_KERNELers/clocksourceGFP_KERNELlass L95" id="L95" class="line" name="L95">  953u
 230                dev_err(&feature3 = dev, &u2.c#L157" id="L157" class="line" name="L157"> 157
clockevents_register_de3ratingcedENOMEMc#L157" id="L15ENOMEM17" id="L217" class="line" name="L217"> 217cpumask<3a> =  210set_mod3 = sh_mtu2_start_stop_ch3        <3 href="+code=dev_info" c3ass="32ine" name="L1e" class="sref"=irqaction" class="sref">irqaction.s="sref">p,
ce/sh&, L218" id="L218"cu2.c#L157" id="L157" class L95" id="L95" class="line" name="L95">  953sref">p-> 156<> 20e" class="sref"=irqaction" class="sref">s="sref">dev_err(&);
 L95" id="L95" class="line" name="L95">  953sd, 03 href="+code=ret" class=3sref"32rivers/clocksource/sh>->  953s       <3( 210 230e="L173"> 173}
 217p-> 162 1323irqreturn_t /* m3ke su33>(int remova href="drivers/clocksouremova/a>, _mtu2.c#L212" id="L212="+code=fl27" id="L227" class=="+code=fl27" i                      u2.c#L157" id="L157" class id="L214" class="line" name="L214"> 214clock_event_device *<3ref="+cod3=p" class="sref">p, 3);
 230e="L173-" class="sref">EBUSYc#L157" id="L15EBUSY17" i"line" name="L140"> 140sh_mtu2_start_stop_ch(pp3/a>-&gid="L162" class="line" name="L162"> 162       <3ef="+code=clk_get_rate" 3lass=33">memset(3a       <3 = (l27" iflhref=d="L227" class= 212l27" iflhref=ef">irqce" class="sref">clock_event_device *<3r       <3pan class="comment">/* &3uot;P33_mtu2.c#L210""L107"> 107   oba href="drivers/  oba/a>,ksource/shrqaction.,=t;rate<3="drivers3clocksource/sh_mtu2.c#L130" id33230"> 230"L107"> 107 remova href="drivers/remova/a>,source/shrqaction.remova href="drivers/clocksouremova/a>,e=t;rate<3ass="sref3>p, );
iource/shrqce" class="sref">clock_event_device *<3ass="sref3>p, ced);
 220  shrqaocksource/sh_mtu2.c#L158" vicename,3ass="sref3>p,  210p,  217p, dev_info(&p, /a> *"nisource/sh_mtu2.c 212"nisu2.c#"sre id="L214" class="line" name="L214"> 214 = /* e3able 34f" class="sref">container_of(s="+code=flhref=f"L227"> s="sref">p,="+code=flhref=f"L227"> a>,  212l27" iflhref=d="L227" class= 212l27" iflhref=ef">sL95" id="L95" class="line" name="L95">  953ref="+cod3=p" class="sref">p, 3);
 180sh_mtu2_clock_event_mo3e="L151">3151}
clock_event_mode<__exisource/sh_mtu2.c_fexis02" ie" class="sref" 212exisource/sh_mtu2.c 212exisu2.c#"sre id="L214" class="line" name="L214"> 214container_of(cpumask_of(="+code=flhref=fun="drivers="sref">p,="+code=flhref=fun="drivera>,  212l27" iflhref=d="L227" class= 212l27" iflhref=ef">sL95" id="L95" class="line" name="L95">  953ef="+code3sh_mtu2_priv" class="sre3">sh_35175" id="L175" class="line" name="L175"> 175/* 3isabl35ef">dev_info(&l27" iflhref=d="L227" class= 212l27" iflhref=ef">sL95" id="L95" class="line" name="L95">  953e> = p, 3);
"nisource/sh_mtu2.c 212"nisu2.csL95" id="L95" class="line" name="L95">  953emtu2.c#L3pan class="comment">/* s3op cl35river->exisource/sh_mtu2.c 212exisu2.csL95" id="L95" class="line" name="L95">  953eef="+cod3id="L159" class="line" n3me="L35ed->n3"sref">p<3a>->clockevents_register_de3>
clockevents_register_de3>ss="sref3href="+code=irqreturn_t"3class3"sref"L218" id="L218"MODULE_LICENSE    default:
clockevents_register_de3>tatic vo3lass="sref">sh_mtu2_inte3rupt<36class


The original LXR software by th6" class="lhttp://eventsodege.net/projects/lxrs>LXR 140"unisylass14this ex "> 1al ref"ion by class="lmailto:lxr@>shux.no">lxr@>shux.nolass.
lxr.>shux.no kindly hoived by class="lhttp://www.redpill->shpro.no">Redpill Lshpro ASlass14providv< of Lshux 14nsul+cod and o airqas ser7" is sincerive5.