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