iio-trig-bfin-timer.c revision 398fd22b6b94cb15c1c299bceecd63644a1b17b4
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 = kzalloc(sizeof(*st), GFP_KERNEL); 186 if (st == NULL) { 187 ret = -ENOMEM; 188 goto out; 189 } 190 191 st->irq = platform_get_irq(pdev, 0); 192 if (!st->irq) { 193 dev_err(&pdev->dev, "No IRQs specified"); 194 ret = -ENODEV; 195 goto out1; 196 } 197 198 ret = iio_bfin_tmr_get_number(st->irq); 199 if (ret < 0) 200 goto out1; 201 202 st->timer_num = ret; 203 st->t = &iio_bfin_timer_code[st->timer_num]; 204 205 st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num); 206 if (!st->trig) { 207 ret = -ENOMEM; 208 goto out1; 209 } 210 211 st->trig->ops = &iio_bfin_tmr_trigger_ops; 212 st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups; 213 iio_trigger_set_drvdata(st->trig, st); 214 ret = iio_trigger_register(st->trig); 215 if (ret) 216 goto out2; 217 218 ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr, 219 0, st->trig->name, st); 220 if (ret) { 221 dev_err(&pdev->dev, 222 "request IRQ-%d failed", st->irq); 223 goto out4; 224 } 225 226 config = PWM_OUT | PERIOD_CNT | IRQ_ENA; 227 228 if (pdata && pdata->output_enable) { 229 unsigned long long val; 230 231 st->output_enable = true; 232 233 ret = peripheral_request(st->t->pin, st->trig->name); 234 if (ret) 235 goto out_free_irq; 236 237 val = (unsigned long long)get_sclk() * pdata->duty_ns; 238 do_div(val, NSEC_PER_SEC); 239 st->duty = val; 240 241 /** 242 * The interrupt will be generated at the end of the period, 243 * since we want the interrupt to be generated at end of the 244 * pulse we invert both polarity and duty cycle, so that the 245 * pulse will be generated directly before the interrupt. 246 */ 247 if (pdata->active_low) 248 config |= PULSE_HI; 249 } else { 250 st->duty = 1; 251 config |= OUT_DIS; 252 } 253 254 set_gptimer_config(st->t->id, config); 255 256 dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d", 257 st->timer_num, st->irq); 258 platform_set_drvdata(pdev, st); 259 260 return 0; 261out_free_irq: 262 free_irq(st->irq, st); 263out4: 264 iio_trigger_unregister(st->trig); 265out2: 266 iio_trigger_put(st->trig); 267out1: 268 kfree(st); 269out: 270 return ret; 271} 272 273static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev) 274{ 275 struct bfin_tmr_state *st = platform_get_drvdata(pdev); 276 277 disable_gptimers(st->t->bit); 278 if (st->output_enable) 279 peripheral_free(st->t->pin); 280 free_irq(st->irq, st); 281 iio_trigger_unregister(st->trig); 282 iio_trigger_put(st->trig); 283 kfree(st); 284 285 return 0; 286} 287 288static struct platform_driver iio_bfin_tmr_trigger_driver = { 289 .driver = { 290 .name = "iio_bfin_tmr_trigger", 291 .owner = THIS_MODULE, 292 }, 293 .probe = iio_bfin_tmr_trigger_probe, 294 .remove = iio_bfin_tmr_trigger_remove, 295}; 296 297module_platform_driver(iio_bfin_tmr_trigger_driver); 298 299MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 300MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem"); 301MODULE_LICENSE("GPL v2"); 302MODULE_ALIAS("platform:iio-trig-bfin-timer"); 303