1/* 2 * Rotary counter driver for Analog Devices Blackfin Processors 3 * 4 * Copyright 2008-2009 Analog Devices Inc. 5 * Licensed under the GPL-2 or later. 6 */ 7 8#include <linux/module.h> 9#include <linux/interrupt.h> 10#include <linux/io.h> 11#include <linux/irq.h> 12#include <linux/pm.h> 13#include <linux/platform_device.h> 14#include <linux/input.h> 15#include <linux/slab.h> 16#include <linux/platform_data/bfin_rotary.h> 17 18#include <asm/portmux.h> 19 20#define CNT_CONFIG_OFF 0 /* CNT Config Offset */ 21#define CNT_IMASK_OFF 4 /* CNT Interrupt Mask Offset */ 22#define CNT_STATUS_OFF 8 /* CNT Status Offset */ 23#define CNT_COMMAND_OFF 12 /* CNT Command Offset */ 24#define CNT_DEBOUNCE_OFF 16 /* CNT Debounce Offset */ 25#define CNT_COUNTER_OFF 20 /* CNT Counter Offset */ 26#define CNT_MAX_OFF 24 /* CNT Maximum Count Offset */ 27#define CNT_MIN_OFF 28 /* CNT Minimum Count Offset */ 28 29struct bfin_rot { 30 struct input_dev *input; 31 void __iomem *base; 32 int irq; 33 unsigned int up_key; 34 unsigned int down_key; 35 unsigned int button_key; 36 unsigned int rel_code; 37 38 unsigned short mode; 39 unsigned short debounce; 40 41 unsigned short cnt_config; 42 unsigned short cnt_imask; 43 unsigned short cnt_debounce; 44}; 45 46static void report_key_event(struct input_dev *input, int keycode) 47{ 48 /* simulate a press-n-release */ 49 input_report_key(input, keycode, 1); 50 input_sync(input); 51 input_report_key(input, keycode, 0); 52 input_sync(input); 53} 54 55static void report_rotary_event(struct bfin_rot *rotary, int delta) 56{ 57 struct input_dev *input = rotary->input; 58 59 if (rotary->up_key) { 60 report_key_event(input, 61 delta > 0 ? rotary->up_key : rotary->down_key); 62 } else { 63 input_report_rel(input, rotary->rel_code, delta); 64 input_sync(input); 65 } 66} 67 68static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) 69{ 70 struct bfin_rot *rotary = dev_id; 71 int delta; 72 73 switch (readw(rotary->base + CNT_STATUS_OFF)) { 74 75 case ICII: 76 break; 77 78 case UCII: 79 case DCII: 80 delta = readl(rotary->base + CNT_COUNTER_OFF); 81 if (delta) 82 report_rotary_event(rotary, delta); 83 break; 84 85 case CZMII: 86 report_key_event(rotary->input, rotary->button_key); 87 break; 88 89 default: 90 break; 91 } 92 93 writew(W1LCNT_ZERO, rotary->base + CNT_COMMAND_OFF); /* Clear COUNTER */ 94 writew(-1, rotary->base + CNT_STATUS_OFF); /* Clear STATUS */ 95 96 return IRQ_HANDLED; 97} 98 99static int bfin_rotary_open(struct input_dev *input) 100{ 101 struct bfin_rot *rotary = input_get_drvdata(input); 102 unsigned short val; 103 104 if (rotary->mode & ROT_DEBE) 105 writew(rotary->debounce & DPRESCALE, 106 rotary->base + CNT_DEBOUNCE_OFF); 107 108 writew(rotary->mode & ~CNTE, rotary->base + CNT_CONFIG_OFF); 109 110 val = UCIE | DCIE; 111 if (rotary->button_key) 112 val |= CZMIE; 113 writew(val, rotary->base + CNT_IMASK_OFF); 114 115 writew(rotary->mode | CNTE, rotary->base + CNT_CONFIG_OFF); 116 117 return 0; 118} 119 120static void bfin_rotary_close(struct input_dev *input) 121{ 122 struct bfin_rot *rotary = input_get_drvdata(input); 123 124 writew(0, rotary->base + CNT_CONFIG_OFF); 125 writew(0, rotary->base + CNT_IMASK_OFF); 126} 127 128static void bfin_rotary_free_action(void *data) 129{ 130 peripheral_free_list(data); 131} 132 133static int bfin_rotary_probe(struct platform_device *pdev) 134{ 135 struct device *dev = &pdev->dev; 136 const struct bfin_rotary_platform_data *pdata = dev_get_platdata(dev); 137 struct bfin_rot *rotary; 138 struct resource *res; 139 struct input_dev *input; 140 int error; 141 142 /* Basic validation */ 143 if ((pdata->rotary_up_key && !pdata->rotary_down_key) || 144 (!pdata->rotary_up_key && pdata->rotary_down_key)) { 145 return -EINVAL; 146 } 147 148 if (pdata->pin_list) { 149 error = peripheral_request_list(pdata->pin_list, 150 dev_name(&pdev->dev)); 151 if (error) { 152 dev_err(dev, "requesting peripherals failed: %d\n", 153 error); 154 return error; 155 } 156 157 error = devm_add_action(dev, bfin_rotary_free_action, 158 pdata->pin_list); 159 if (error) { 160 dev_err(dev, "setting cleanup action failed: %d\n", 161 error); 162 peripheral_free_list(pdata->pin_list); 163 return error; 164 } 165 } 166 167 rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL); 168 if (!rotary) 169 return -ENOMEM; 170 171 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 172 rotary->base = devm_ioremap_resource(dev, res); 173 if (IS_ERR(rotary->base)) 174 return PTR_ERR(rotary->base); 175 176 input = devm_input_allocate_device(dev); 177 if (!input) 178 return -ENOMEM; 179 180 rotary->input = input; 181 182 rotary->up_key = pdata->rotary_up_key; 183 rotary->down_key = pdata->rotary_down_key; 184 rotary->button_key = pdata->rotary_button_key; 185 rotary->rel_code = pdata->rotary_rel_code; 186 187 rotary->mode = pdata->mode; 188 rotary->debounce = pdata->debounce; 189 190 input->name = pdev->name; 191 input->phys = "bfin-rotary/input0"; 192 input->dev.parent = &pdev->dev; 193 194 input_set_drvdata(input, rotary); 195 196 input->id.bustype = BUS_HOST; 197 input->id.vendor = 0x0001; 198 input->id.product = 0x0001; 199 input->id.version = 0x0100; 200 201 input->open = bfin_rotary_open; 202 input->close = bfin_rotary_close; 203 204 if (rotary->up_key) { 205 __set_bit(EV_KEY, input->evbit); 206 __set_bit(rotary->up_key, input->keybit); 207 __set_bit(rotary->down_key, input->keybit); 208 } else { 209 __set_bit(EV_REL, input->evbit); 210 __set_bit(rotary->rel_code, input->relbit); 211 } 212 213 if (rotary->button_key) { 214 __set_bit(EV_KEY, input->evbit); 215 __set_bit(rotary->button_key, input->keybit); 216 } 217 218 /* Quiesce the device before requesting irq */ 219 bfin_rotary_close(input); 220 221 rotary->irq = platform_get_irq(pdev, 0); 222 if (rotary->irq < 0) { 223 dev_err(dev, "No rotary IRQ specified\n"); 224 return -ENOENT; 225 } 226 227 error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr, 228 0, dev_name(dev), rotary); 229 if (error) { 230 dev_err(dev, "unable to claim irq %d; error %d\n", 231 rotary->irq, error); 232 return error; 233 } 234 235 error = input_register_device(input); 236 if (error) { 237 dev_err(dev, "unable to register input device (%d)\n", error); 238 return error; 239 } 240 241 platform_set_drvdata(pdev, rotary); 242 device_init_wakeup(&pdev->dev, 1); 243 244 return 0; 245} 246 247static int __maybe_unused bfin_rotary_suspend(struct device *dev) 248{ 249 struct platform_device *pdev = to_platform_device(dev); 250 struct bfin_rot *rotary = platform_get_drvdata(pdev); 251 252 rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF); 253 rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF); 254 rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF); 255 256 if (device_may_wakeup(&pdev->dev)) 257 enable_irq_wake(rotary->irq); 258 259 return 0; 260} 261 262static int __maybe_unused bfin_rotary_resume(struct device *dev) 263{ 264 struct platform_device *pdev = to_platform_device(dev); 265 struct bfin_rot *rotary = platform_get_drvdata(pdev); 266 267 writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF); 268 writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF); 269 writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF); 270 271 if (device_may_wakeup(&pdev->dev)) 272 disable_irq_wake(rotary->irq); 273 274 if (rotary->cnt_config & CNTE) 275 writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF); 276 277 return 0; 278} 279 280static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops, 281 bfin_rotary_suspend, bfin_rotary_resume); 282 283static struct platform_driver bfin_rotary_device_driver = { 284 .probe = bfin_rotary_probe, 285 .driver = { 286 .name = "bfin-rotary", 287 .pm = &bfin_rotary_pm_ops, 288 }, 289}; 290module_platform_driver(bfin_rotary_device_driver); 291 292MODULE_LICENSE("GPL"); 293MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 294MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); 295MODULE_ALIAS("platform:bfin-rotary"); 296