linux/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Analog Devices Inc.
   3 *
   4 * Licensed under the GPL-2.
   5 *
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/platform_device.h>
  11#include <linux/slab.h>
  12#include <linux/interrupt.h>
  13#include <linux/irq.h>
  14#include <linux/delay.h>
  15
  16#include <asm/gptimers.h>
  17#include <asm/portmux.h>
  18
  19#include <linux/iio/iio.h>
  20#include <linux/iio/trigger.h>
  21
  22#include "iio-trig-bfin-timer.h"
  23
  24struct bfin_timer {
  25        unsigned short id, bit;
  26        unsigned long irqbit;
  27        int irq;
  28        int pin;
  29};
  30
  31/*
  32 * this covers all hardware timer configurations on
  33 * all Blackfin derivatives out there today
  34 */
  35
  36static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = {
  37        {TIMER0_id,  TIMER0bit,  TIMER_STATUS_TIMIL0,  IRQ_TIMER0, P_TMR0},
  38        {TIMER1_id,  TIMER1bit,  TIMER_STATUS_TIMIL1,  IRQ_TIMER1, P_TMR1},
  39        {TIMER2_id,  TIMER2bit,  TIMER_STATUS_TIMIL2,  IRQ_TIMER2, P_TMR2},
  40#if (MAX_BLACKFIN_GPTIMERS > 3)
  41        {TIMER3_id,  TIMER3bit,  TIMER_STATUS_TIMIL3,  IRQ_TIMER3, P_TMR3},
  42        {TIMER4_id,  TIMER4bit,  TIMER_STATUS_TIMIL4,  IRQ_TIMER4, P_TMR4},
  43        {TIMER5_id,  TIMER5bit,  TIMER_STATUS_TIMIL5,  IRQ_TIMER5, P_TMR5},
  44        {TIMER6_id,  TIMER6bit,  TIMER_STATUS_TIMIL6,  IRQ_TIMER6, P_TMR6},
  45        {TIMER7_id,  TIMER7bit,  TIMER_STATUS_TIMIL7,  IRQ_TIMER7, P_TMR7},
  46#endif
  47#if (MAX_BLACKFIN_GPTIMERS > 8)
  48        {TIMER8_id,  TIMER8bit,  TIMER_STATUS_TIMIL8,  IRQ_TIMER8, P_TMR8},
  49        {TIMER9_id,  TIMER9bit,  TIMER_STATUS_TIMIL9,  IRQ_TIMER9, P_TMR9},
  50        {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10},
  51#if (MAX_BLACKFIN_GPTIMERS > 11)
  52        {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11},
  53#endif
  54#endif
  55};
  56
  57struct bfin_tmr_state {
  58        struct iio_trigger *trig;
  59        struct bfin_timer *t;
  60        unsigned timer_num;
  61        bool output_enable;
  62        unsigned int duty;
  63        int irq;
  64};
  65
  66static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state)
  67{
  68        struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
  69
  70        if (get_gptimer_period(st->t->id) == 0)
  71                return -EINVAL;
  72
  73        if (state)
  74                enable_gptimers(st->t->bit);
  75        else
  76                disable_gptimers(st->t->bit);
  77
  78        return 0;
  79}
  80
  81static ssize_t iio_bfin_tmr_frequency_store(struct device *dev,
  82                struct device_attribute *attr, const char *buf, size_t count)
  83{
  84        struct iio_trigger *trig = to_iio_trigger(dev);
  85        struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
  86        unsigned long val;
  87        bool enabled;
  88        int ret;
  89
  90        ret = strict_strtoul(buf, 10, &val);
  91        if (ret)
  92                goto error_ret;
  93
  94        if (val > 100000) {
  95                ret = -EINVAL;
  96                goto error_ret;
  97        }
  98
  99        enabled = get_enabled_gptimers() & st->t->bit;
 100
 101        if (enabled)
 102                disable_gptimers(st->t->bit);
 103
 104        if (!val)
 105                goto error_ret;
 106
 107        val = get_sclk() / val;
 108        if (val <= 4 || val <= st->duty) {
 109                ret = -EINVAL;
 110                goto error_ret;
 111        }
 112
 113        set_gptimer_period(st->t->id, val);
 114        set_gptimer_pwidth(st->t->id, val - st->duty);
 115
 116        if (enabled)
 117                enable_gptimers(st->t->bit);
 118
 119error_ret:
 120        return ret ? ret : count;
 121}
 122
 123static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
 124                                 struct device_attribute *attr,
 125                                 char *buf)
 126{
 127        struct iio_trigger *trig = to_iio_trigger(dev);
 128        struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
 129        unsigned int period = get_gptimer_period(st->t->id);
 130        unsigned long val;
 131
 132        if (period == 0)
 133                val = 0;
 134        else
 135                val = get_sclk() / get_gptimer_period(st->t->id);
 136
 137        return sprintf(buf, "%lu\n", val);
 138}
 139
 140static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
 141                   iio_bfin_tmr_frequency_store);
 142
 143static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
 144        &dev_attr_frequency.attr,
 145        NULL,
 146};
 147
 148static const struct attribute_group iio_bfin_tmr_trigger_attr_group = {
 149        .attrs = iio_bfin_tmr_trigger_attrs,
 150};
 151
 152static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
 153        &iio_bfin_tmr_trigger_attr_group,
 154        NULL
 155};
 156
 157static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
 158{
 159        struct bfin_tmr_state *st = devid;
 160
 161        clear_gptimer_intr(st->t->id);
 162        iio_trigger_poll(st->trig);
 163
 164        return IRQ_HANDLED;
 165}
 166
 167static int iio_bfin_tmr_get_number(int irq)
 168{
 169        int i;
 170
 171        for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++)
 172                if (iio_bfin_timer_code[i].irq == irq)
 173                        return i;
 174
 175        return -ENODEV;
 176}
 177
 178static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
 179        .owner = THIS_MODULE,
 180        .set_trigger_state = iio_bfin_tmr_set_state,
 181};
 182
 183static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
 184{
 185        struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data;
 186        struct bfin_tmr_state *st;
 187        unsigned int config;
 188        int ret;
 189
 190        st = kzalloc(sizeof(*st), GFP_KERNEL);
 191        if (st == NULL) {
 192                ret = -ENOMEM;
 193                goto out;
 194        }
 195
 196        st->irq = platform_get_irq(pdev, 0);
 197        if (!st->irq) {
 198                dev_err(&pdev->dev, "No IRQs specified");
 199                ret = -ENODEV;
 200                goto out1;
 201        }
 202
 203        ret = iio_bfin_tmr_get_number(st->irq);
 204        if (ret < 0)
 205                goto out1;
 206
 207        st->timer_num = ret;
 208        st->t = &iio_bfin_timer_code[st->timer_num];
 209
 210        st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num);
 211        if (!st->trig) {
 212                ret = -ENOMEM;
 213                goto out1;
 214        }
 215
 216        st->trig->ops = &iio_bfin_tmr_trigger_ops;
 217        st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups;
 218        iio_trigger_set_drvdata(st->trig, st);
 219        ret = iio_trigger_register(st->trig);
 220        if (ret)
 221                goto out2;
 222
 223        ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr,
 224                          0, st->trig->name, st);
 225        if (ret) {
 226                dev_err(&pdev->dev,
 227                        "request IRQ-%d failed", st->irq);
 228                goto out4;
 229        }
 230
 231        config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
 232
 233        if (pdata && pdata->output_enable) {
 234                unsigned long long val;
 235
 236                st->output_enable = true;
 237
 238                ret = peripheral_request(st->t->pin, st->trig->name);
 239                if (ret)
 240                        goto out_free_irq;
 241
 242                val = (unsigned long long)get_sclk() * pdata->duty_ns;
 243                do_div(val, NSEC_PER_SEC);
 244                st->duty = val;
 245
 246                /**
 247                 * The interrupt will be generated at the end of the period,
 248                 * since we want the interrupt to be generated at end of the
 249                 * pulse we invert both polarity and duty cycle, so that the
 250                 * pulse will be generated directly before the interrupt.
 251                 */
 252                if (pdata->active_low)
 253                        config |= PULSE_HI;
 254        } else {
 255                st->duty = 1;
 256                config |= OUT_DIS;
 257        }
 258
 259        set_gptimer_config(st->t->id, config);
 260
 261        dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
 262                 st->timer_num, st->irq);
 263        platform_set_drvdata(pdev, st);
 264
 265        return 0;
 266out_free_irq:
 267        free_irq(st->irq, st);
 268out4:
 269        iio_trigger_unregister(st->trig);
 270out2:
 271        iio_trigger_put(st->trig);
 272out1:
 273        kfree(st);
 274out:
 275        return ret;
 276}
 277
 278static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
 279{
 280        struct bfin_tmr_state *st = platform_get_drvdata(pdev);
 281
 282        disable_gptimers(st->t->bit);
 283        if (st->output_enable)
 284                peripheral_free(st->t->pin);
 285        free_irq(st->irq, st);
 286        iio_trigger_unregister(st->trig);
 287        iio_trigger_put(st->trig);
 288        kfree(st);
 289
 290        return 0;
 291}
 292
 293static struct platform_driver iio_bfin_tmr_trigger_driver = {
 294        .driver = {
 295                .name = "iio_bfin_tmr_trigger",
 296                .owner = THIS_MODULE,
 297        },
 298        .probe = iio_bfin_tmr_trigger_probe,
 299        .remove = iio_bfin_tmr_trigger_remove,
 300};
 301
 302module_platform_driver(iio_bfin_tmr_trigger_driver);
 303
 304MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 305MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem");
 306MODULE_LICENSE("GPL v2");
 307MODULE_ALIAS("platform:iio-trig-bfin-timer");
 308