Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

staging: IIO: trigger: New Blackfin specific trigger driver iio-trig-bfin-timer

This driver allows any Blackfin system timer to be used as IIO trigger.
It supports trigger rates from 0 to 100kHz in Hz resolution.

Changes since V1:
IIO: trigger: Apply review feedback

Add comment explaining Blackfin hardware timer configurations
Fix frequency_store, don't return -EINVAL for frequency set to 0

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Michael Hennerich and committed by
Greg Kroah-Hartman
ea5dbf96 0772268a

+263
+10
drivers/staging/iio/trigger/Kconfig
··· 28 28 To compile this driver as a module, choose M here: the 29 29 module will be called iio-trig-sysfs. 30 30 31 + config IIO_BFIN_TMR_TRIGGER 32 + tristate "Blackfin TIMER trigger" 33 + depends on BLACKFIN 34 + help 35 + Provides support for using a Blackfin timer as IIO triggers. 36 + If unsure, say N (but it's safe to say "Y"). 37 + 38 + To compile this driver as a module, choose M here: the 39 + module will be called iio-trig-bfin-timer. 40 + 31 41 endif # IIO_TRIGGER
+1
drivers/staging/iio/trigger/Makefile
··· 5 5 obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o 6 6 obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o 7 7 obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o 8 + obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o
+252
drivers/staging/iio/trigger/iio-trig-bfin-timer.c
··· 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 + 18 + #include "../iio.h" 19 + #include "../trigger.h" 20 + 21 + struct bfin_timer { 22 + unsigned short id, bit; 23 + unsigned long irqbit; 24 + int irq; 25 + }; 26 + 27 + /* 28 + * this covers all hardware timer configurations on 29 + * all Blackfin derivatives out there today 30 + */ 31 + 32 + static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = { 33 + {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0}, 34 + {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1}, 35 + {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2}, 36 + #if (MAX_BLACKFIN_GPTIMERS > 3) 37 + {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3}, 38 + {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4}, 39 + {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5}, 40 + {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6}, 41 + {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7}, 42 + #endif 43 + #if (MAX_BLACKFIN_GPTIMERS > 8) 44 + {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8}, 45 + {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9}, 46 + {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10}, 47 + #if (MAX_BLACKFIN_GPTIMERS > 11) 48 + {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11}, 49 + #endif 50 + #endif 51 + }; 52 + 53 + struct bfin_tmr_state { 54 + struct iio_trigger *trig; 55 + struct bfin_timer *t; 56 + unsigned timer_num; 57 + int irq; 58 + }; 59 + 60 + static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, 61 + struct device_attribute *attr, const char *buf, size_t count) 62 + { 63 + struct iio_trigger *trig = dev_get_drvdata(dev); 64 + struct bfin_tmr_state *st = trig->private_data; 65 + long val; 66 + int ret; 67 + 68 + ret = strict_strtoul(buf, 10, &val); 69 + if (ret) 70 + goto error_ret; 71 + 72 + if (val > 100000) { 73 + ret = -EINVAL; 74 + goto error_ret; 75 + } 76 + 77 + disable_gptimers(st->t->bit); 78 + 79 + if (!val) 80 + goto error_ret; 81 + 82 + val = get_sclk() / val; 83 + if (val <= 4) { 84 + ret = -EINVAL; 85 + goto error_ret; 86 + } 87 + 88 + set_gptimer_period(st->t->id, val); 89 + set_gptimer_pwidth(st->t->id, 1); 90 + enable_gptimers(st->t->bit); 91 + 92 + error_ret: 93 + return ret ? ret : count; 94 + } 95 + 96 + static ssize_t iio_bfin_tmr_frequency_show(struct device *dev, 97 + struct device_attribute *attr, 98 + char *buf) 99 + { 100 + struct iio_trigger *trig = dev_get_drvdata(dev); 101 + struct bfin_tmr_state *st = trig->private_data; 102 + 103 + return sprintf(buf, "%lu\n", 104 + get_sclk() / get_gptimer_period(st->t->id)); 105 + } 106 + 107 + static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show, 108 + iio_bfin_tmr_frequency_store); 109 + static IIO_TRIGGER_NAME_ATTR; 110 + 111 + static struct attribute *iio_bfin_tmr_trigger_attrs[] = { 112 + &dev_attr_frequency.attr, 113 + &dev_attr_name.attr, 114 + NULL, 115 + }; 116 + 117 + static const struct attribute_group iio_bfin_tmr_trigger_attr_group = { 118 + .attrs = iio_bfin_tmr_trigger_attrs, 119 + }; 120 + 121 + 122 + static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid) 123 + { 124 + struct bfin_tmr_state *st = devid; 125 + 126 + clear_gptimer_intr(st->t->id); 127 + iio_trigger_poll(st->trig, 0); 128 + 129 + return IRQ_HANDLED; 130 + } 131 + 132 + static int iio_bfin_tmr_get_number(int irq) 133 + { 134 + int i; 135 + 136 + for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++) 137 + if (iio_bfin_timer_code[i].irq == irq) 138 + return i; 139 + 140 + return -ENODEV; 141 + } 142 + 143 + static int __devinit iio_bfin_tmr_trigger_probe(struct platform_device *pdev) 144 + { 145 + struct bfin_tmr_state *st; 146 + int ret; 147 + 148 + st = kzalloc(sizeof(*st), GFP_KERNEL); 149 + if (st == NULL) { 150 + ret = -ENOMEM; 151 + goto out; 152 + } 153 + 154 + st->irq = platform_get_irq(pdev, 0); 155 + if (!st->irq) { 156 + dev_err(&pdev->dev, "No IRQs specified"); 157 + ret = -ENODEV; 158 + goto out1; 159 + } 160 + 161 + ret = iio_bfin_tmr_get_number(st->irq); 162 + if (ret < 0) 163 + goto out1; 164 + 165 + st->timer_num = ret; 166 + st->t = &iio_bfin_timer_code[st->timer_num]; 167 + 168 + st->trig = iio_allocate_trigger(); 169 + if (!st->trig) { 170 + ret = -ENOMEM; 171 + goto out1; 172 + } 173 + 174 + st->trig->private_data = st; 175 + st->trig->control_attrs = &iio_bfin_tmr_trigger_attr_group; 176 + st->trig->owner = THIS_MODULE; 177 + st->trig->name = kasprintf(GFP_KERNEL, "bfintmr%d", st->timer_num); 178 + if (st->trig->name == NULL) { 179 + ret = -ENOMEM; 180 + goto out2; 181 + } 182 + 183 + ret = iio_trigger_register(st->trig); 184 + if (ret) 185 + goto out3; 186 + 187 + ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr, 188 + 0, st->trig->name, st); 189 + if (ret) { 190 + dev_err(&pdev->dev, 191 + "request IRQ-%d failed", st->irq); 192 + goto out4; 193 + } 194 + 195 + set_gptimer_config(st->t->id, OUT_DIS | PWM_OUT | PERIOD_CNT | IRQ_ENA); 196 + 197 + dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d", 198 + st->timer_num, st->irq); 199 + platform_set_drvdata(pdev, st); 200 + 201 + return 0; 202 + out4: 203 + iio_trigger_unregister(st->trig); 204 + out3: 205 + kfree(st->trig->name); 206 + out2: 207 + iio_put_trigger(st->trig); 208 + out1: 209 + kfree(st); 210 + out: 211 + return ret; 212 + } 213 + 214 + static int __devexit iio_bfin_tmr_trigger_remove(struct platform_device *pdev) 215 + { 216 + struct bfin_tmr_state *st = platform_get_drvdata(pdev); 217 + 218 + disable_gptimers(st->t->bit); 219 + free_irq(st->irq, st); 220 + iio_trigger_unregister(st->trig); 221 + kfree(st->trig->name); 222 + iio_put_trigger(st->trig); 223 + kfree(st); 224 + 225 + return 0; 226 + } 227 + 228 + static struct platform_driver iio_bfin_tmr_trigger_driver = { 229 + .driver = { 230 + .name = "iio_bfin_tmr_trigger", 231 + .owner = THIS_MODULE, 232 + }, 233 + .probe = iio_bfin_tmr_trigger_probe, 234 + .remove = __devexit_p(iio_bfin_tmr_trigger_remove), 235 + }; 236 + 237 + static int __init iio_bfin_tmr_trig_init(void) 238 + { 239 + return platform_driver_register(&iio_bfin_tmr_trigger_driver); 240 + } 241 + module_init(iio_bfin_tmr_trig_init); 242 + 243 + static void __exit iio_bfin_tmr_trig_exit(void) 244 + { 245 + platform_driver_unregister(&iio_bfin_tmr_trigger_driver); 246 + } 247 + module_exit(iio_bfin_tmr_trig_exit); 248 + 249 + MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 250 + MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem"); 251 + MODULE_LICENSE("GPL v2"); 252 + MODULE_ALIAS("platform:iio-trig-bfin-timer");