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 int            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,
  83                                            const char *buf, size_t count)
  84{
  85        struct iio_trigger *trig = to_iio_trigger(dev);
  86        struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
  87        unsigned int val;
  88        bool enabled;
  89        int ret;
  90
  91        ret = kstrtouint(buf, 10, &val);
  92        if (ret)
  93                return ret;
  94
  95        if (val > 100000)
  96                return -EINVAL;
  97
  98        enabled = get_enabled_gptimers() & st->t->bit;
  99
 100        if (enabled)
 101                disable_gptimers(st->t->bit);
 102
 103        if (!val)
 104                return count;
 105
 106        val = get_sclk() / val;
 107        if (val <= 4 || val <= st->duty)
 108                return -EINVAL;
 109
 110        set_gptimer_period(st->t->id, val);
 111        set_gptimer_pwidth(st->t->id, val - st->duty);
 112
 113        if (enabled)
 114                enable_gptimers(st->t->bit);
 115
 116        return count;
 117}
 118
 119static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
 120                                           struct device_attribute *attr,
 121                                           char *buf)
 122{
 123        struct iio_trigger *trig = to_iio_trigger(dev);
 124        struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
 125        unsigned int period = get_gptimer_period(st->t->id);
 126        unsigned long val;
 127
 128        if (!period)
 129                val = 0;
 130        else
 131                val = get_sclk() / get_gptimer_period(st->t->id);
 132
 133        return sprintf(buf, "%lu\n", val);
 134}
 135
 136static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
 137                   iio_bfin_tmr_frequency_store);
 138
 139static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
 140        &dev_attr_frequency.attr,
 141        NULL,
 142};
 143
 144static const struct attribute_group iio_bfin_tmr_trigger_attr_group = {
 145        .attrs = iio_bfin_tmr_trigger_attrs,
 146};
 147
 148static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
 149        &iio_bfin_tmr_trigger_attr_group,
 150        NULL
 151};
 152
 153static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
 154{
 155        struct bfin_tmr_state *st = devid;
 156
 157        clear_gptimer_intr(st->t->id);
 158        iio_trigger_poll(st->trig);
 159
 160        return IRQ_HANDLED;
 161}
 162
 163static int iio_bfin_tmr_get_number(int irq)
 164{
 165        int i;
 166
 167        for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++)
 168                if (iio_bfin_timer_code[i].irq == irq)
 169                        return i;
 170
 171        return -ENODEV;
 172}
 173
 174static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
 175        .owner = THIS_MODULE,
 176        .set_trigger_state = iio_bfin_tmr_set_state,
 177};
 178
 179static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
 180{
 181        struct iio_bfin_timer_trigger_pdata *pdata;
 182        struct bfin_tmr_state *st;
 183        unsigned int config;
 184        int ret;
 185
 186        st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
 187        if (!st)
 188                return -ENOMEM;
 189
 190        st->irq = platform_get_irq(pdev, 0);
 191        if (!st->irq) {
 192                dev_err(&pdev->dev, "No IRQs specified");
 193                return -ENODEV;
 194        }
 195
 196        ret = iio_bfin_tmr_get_number(st->irq);
 197        if (ret < 0)
 198                return ret;
 199
 200        st->timer_num = ret;
 201        st->t = &iio_bfin_timer_code[st->timer_num];
 202
 203        st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num);
 204        if (!st->trig)
 205                return -ENOMEM;
 206
 207        st->trig->ops = &iio_bfin_tmr_trigger_ops;
 208        st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups;
 209        iio_trigger_set_drvdata(st->trig, st);
 210        ret = iio_trigger_register(st->trig);
 211        if (ret)
 212                goto out;
 213
 214        ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr,
 215                          0, st->trig->name, st);
 216        if (ret) {
 217                dev_err(&pdev->dev,
 218                        "request IRQ-%d failed", st->irq);
 219                goto out1;
 220        }
 221
 222        config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
 223
 224        pdata = dev_get_platdata(&pdev->dev);
 225        if (pdata && pdata->output_enable) {
 226                unsigned long long val;
 227
 228                st->output_enable = true;
 229
 230                ret = peripheral_request(st->t->pin, st->trig->name);
 231                if (ret)
 232                        goto out_free_irq;
 233
 234                val = (unsigned long long)get_sclk() * pdata->duty_ns;
 235                do_div(val, NSEC_PER_SEC);
 236                st->duty = val;
 237
 238                /**
 239                 * The interrupt will be generated at the end of the period,
 240                 * since we want the interrupt to be generated at end of the
 241                 * pulse we invert both polarity and duty cycle, so that the
 242                 * pulse will be generated directly before the interrupt.
 243                 */
 244                if (pdata->active_low)
 245                        config |= PULSE_HI;
 246        } else {
 247                st->duty = 1;
 248                config |= OUT_DIS;
 249        }
 250
 251        set_gptimer_config(st->t->id, config);
 252
 253        dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
 254                 st->timer_num, st->irq);
 255        platform_set_drvdata(pdev, st);
 256
 257        return 0;
 258out_free_irq:
 259        free_irq(st->irq, st);
 260out1:
 261        iio_trigger_unregister(st->trig);
 262out:
 263        iio_trigger_put(st->trig);
 264        return ret;
 265}
 266
 267static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
 268{
 269        struct bfin_tmr_state *st = platform_get_drvdata(pdev);
 270
 271        disable_gptimers(st->t->bit);
 272        if (st->output_enable)
 273                peripheral_free(st->t->pin);
 274        free_irq(st->irq, st);
 275        iio_trigger_unregister(st->trig);
 276        iio_trigger_put(st->trig);
 277
 278        return 0;
 279}
 280
 281static struct platform_driver iio_bfin_tmr_trigger_driver = {
 282        .driver = {
 283                .name = "iio_bfin_tmr_trigger",
 284        },
 285        .probe = iio_bfin_tmr_trigger_probe,
 286        .remove = iio_bfin_tmr_trigger_remove,
 287};
 288
 289module_platform_driver(iio_bfin_tmr_trigger_driver);
 290
 291MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 292MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem");
 293MODULE_LICENSE("GPL v2");
 294MODULE_ALIAS("platform:iio-trig-bfin-timer");
 295