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/version.h> 10#include <linux/init.h> 11#include <linux/interrupt.h> 12#include <linux/irq.h> 13#include <linux/pm.h> 14#include <linux/platform_device.h> 15#include <linux/input.h> 16#include <linux/slab.h> 17 18#include <asm/portmux.h> 19#include <asm/bfin_rotary.h> 20 21static const u16 per_cnt[] = { 22 P_CNT_CUD, 23 P_CNT_CDG, 24 P_CNT_CZM, 25 0 26}; 27 28struct bfin_rot { 29 struct input_dev *input; 30 int irq; 31 unsigned int up_key; 32 unsigned int down_key; 33 unsigned int button_key; 34 unsigned int rel_code; 35 unsigned short cnt_config; 36 unsigned short cnt_imask; 37 unsigned short cnt_debounce; 38}; 39 40static void report_key_event(struct input_dev *input, int keycode) 41{ 42 /* simulate a press-n-release */ 43 input_report_key(input, keycode, 1); 44 input_sync(input); 45 input_report_key(input, keycode, 0); 46 input_sync(input); 47} 48 49static void report_rotary_event(struct bfin_rot *rotary, int delta) 50{ 51 struct input_dev *input = rotary->input; 52 53 if (rotary->up_key) { 54 report_key_event(input, 55 delta > 0 ? rotary->up_key : rotary->down_key); 56 } else { 57 input_report_rel(input, rotary->rel_code, delta); 58 input_sync(input); 59 } 60} 61 62static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) 63{ 64 struct platform_device *pdev = dev_id; 65 struct bfin_rot *rotary = platform_get_drvdata(pdev); 66 int delta; 67 68 switch (bfin_read_CNT_STATUS()) { 69 70 case ICII: 71 break; 72 73 case UCII: 74 case DCII: 75 delta = bfin_read_CNT_COUNTER(); 76 if (delta) 77 report_rotary_event(rotary, delta); 78 break; 79 80 case CZMII: 81 report_key_event(rotary->input, rotary->button_key); 82 break; 83 84 default: 85 break; 86 } 87 88 bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */ 89 bfin_write_CNT_STATUS(-1); /* Clear STATUS */ 90 91 return IRQ_HANDLED; 92} 93 94static int __devinit bfin_rotary_probe(struct platform_device *pdev) 95{ 96 struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data; 97 struct bfin_rot *rotary; 98 struct input_dev *input; 99 int error; 100 101 /* Basic validation */ 102 if ((pdata->rotary_up_key && !pdata->rotary_down_key) || 103 (!pdata->rotary_up_key && pdata->rotary_down_key)) { 104 return -EINVAL; 105 } 106 107 error = peripheral_request_list(per_cnt, dev_name(&pdev->dev)); 108 if (error) { 109 dev_err(&pdev->dev, "requesting peripherals failed\n"); 110 return error; 111 } 112 113 rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL); 114 input = input_allocate_device(); 115 if (!rotary || !input) { 116 error = -ENOMEM; 117 goto out1; 118 } 119 120 rotary->input = input; 121 122 rotary->up_key = pdata->rotary_up_key; 123 rotary->down_key = pdata->rotary_down_key; 124 rotary->button_key = pdata->rotary_button_key; 125 rotary->rel_code = pdata->rotary_rel_code; 126 127 error = rotary->irq = platform_get_irq(pdev, 0); 128 if (error < 0) 129 goto out1; 130 131 input->name = pdev->name; 132 input->phys = "bfin-rotary/input0"; 133 input->dev.parent = &pdev->dev; 134 135 input_set_drvdata(input, rotary); 136 137 input->id.bustype = BUS_HOST; 138 input->id.vendor = 0x0001; 139 input->id.product = 0x0001; 140 input->id.version = 0x0100; 141 142 if (rotary->up_key) { 143 __set_bit(EV_KEY, input->evbit); 144 __set_bit(rotary->up_key, input->keybit); 145 __set_bit(rotary->down_key, input->keybit); 146 } else { 147 __set_bit(EV_REL, input->evbit); 148 __set_bit(rotary->rel_code, input->relbit); 149 } 150 151 if (rotary->button_key) { 152 __set_bit(EV_KEY, input->evbit); 153 __set_bit(rotary->button_key, input->keybit); 154 } 155 156 error = request_irq(rotary->irq, bfin_rotary_isr, 157 0, dev_name(&pdev->dev), pdev); 158 if (error) { 159 dev_err(&pdev->dev, 160 "unable to claim irq %d; error %d\n", 161 rotary->irq, error); 162 goto out1; 163 } 164 165 error = input_register_device(input); 166 if (error) { 167 dev_err(&pdev->dev, 168 "unable to register input device (%d)\n", error); 169 goto out2; 170 } 171 172 if (pdata->rotary_button_key) 173 bfin_write_CNT_IMASK(CZMIE); 174 175 if (pdata->mode & ROT_DEBE) 176 bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE); 177 178 if (pdata->mode) 179 bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | 180 (pdata->mode & ~CNTE)); 181 182 bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE); 183 bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE); 184 185 platform_set_drvdata(pdev, rotary); 186 device_init_wakeup(&pdev->dev, 1); 187 188 return 0; 189 190out2: 191 free_irq(rotary->irq, pdev); 192out1: 193 input_free_device(input); 194 kfree(rotary); 195 peripheral_free_list(per_cnt); 196 197 return error; 198} 199 200static int __devexit bfin_rotary_remove(struct platform_device *pdev) 201{ 202 struct bfin_rot *rotary = platform_get_drvdata(pdev); 203 204 bfin_write_CNT_CONFIG(0); 205 bfin_write_CNT_IMASK(0); 206 207 free_irq(rotary->irq, pdev); 208 input_unregister_device(rotary->input); 209 peripheral_free_list(per_cnt); 210 211 kfree(rotary); 212 platform_set_drvdata(pdev, NULL); 213 214 return 0; 215} 216 217#ifdef CONFIG_PM 218static int bfin_rotary_suspend(struct device *dev) 219{ 220 struct platform_device *pdev = to_platform_device(dev); 221 struct bfin_rot *rotary = platform_get_drvdata(pdev); 222 223 rotary->cnt_config = bfin_read_CNT_CONFIG(); 224 rotary->cnt_imask = bfin_read_CNT_IMASK(); 225 rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE(); 226 227 if (device_may_wakeup(&pdev->dev)) 228 enable_irq_wake(rotary->irq); 229 230 return 0; 231} 232 233static int bfin_rotary_resume(struct device *dev) 234{ 235 struct platform_device *pdev = to_platform_device(dev); 236 struct bfin_rot *rotary = platform_get_drvdata(pdev); 237 238 bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce); 239 bfin_write_CNT_IMASK(rotary->cnt_imask); 240 bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE); 241 242 if (device_may_wakeup(&pdev->dev)) 243 disable_irq_wake(rotary->irq); 244 245 if (rotary->cnt_config & CNTE) 246 bfin_write_CNT_CONFIG(rotary->cnt_config); 247 248 return 0; 249} 250 251static const struct dev_pm_ops bfin_rotary_pm_ops = { 252 .suspend = bfin_rotary_suspend, 253 .resume = bfin_rotary_resume, 254}; 255#endif 256 257static struct platform_driver bfin_rotary_device_driver = { 258 .probe = bfin_rotary_probe, 259 .remove = __devexit_p(bfin_rotary_remove), 260 .driver = { 261 .name = "bfin-rotary", 262 .owner = THIS_MODULE, 263#ifdef CONFIG_PM 264 .pm = &bfin_rotary_pm_ops, 265#endif 266 }, 267}; 268 269static int __init bfin_rotary_init(void) 270{ 271 return platform_driver_register(&bfin_rotary_device_driver); 272} 273module_init(bfin_rotary_init); 274 275static void __exit bfin_rotary_exit(void) 276{ 277 platform_driver_unregister(&bfin_rotary_device_driver); 278} 279module_exit(bfin_rotary_exit); 280 281MODULE_LICENSE("GPL"); 282MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 283MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); 284MODULE_ALIAS("platform:bfin-rotary"); 285