Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.13-rc3 294 lines 7.3 kB view raw
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(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_or_reset(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 return error; 163 } 164 } 165 166 rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL); 167 if (!rotary) 168 return -ENOMEM; 169 170 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 171 rotary->base = devm_ioremap_resource(dev, res); 172 if (IS_ERR(rotary->base)) 173 return PTR_ERR(rotary->base); 174 175 input = devm_input_allocate_device(dev); 176 if (!input) 177 return -ENOMEM; 178 179 rotary->input = input; 180 181 rotary->up_key = pdata->rotary_up_key; 182 rotary->down_key = pdata->rotary_down_key; 183 rotary->button_key = pdata->rotary_button_key; 184 rotary->rel_code = pdata->rotary_rel_code; 185 186 rotary->mode = pdata->mode; 187 rotary->debounce = pdata->debounce; 188 189 input->name = pdev->name; 190 input->phys = "bfin-rotary/input0"; 191 input->dev.parent = dev; 192 193 input_set_drvdata(input, rotary); 194 195 input->id.bustype = BUS_HOST; 196 input->id.vendor = 0x0001; 197 input->id.product = 0x0001; 198 input->id.version = 0x0100; 199 200 input->open = bfin_rotary_open; 201 input->close = bfin_rotary_close; 202 203 if (rotary->up_key) { 204 __set_bit(EV_KEY, input->evbit); 205 __set_bit(rotary->up_key, input->keybit); 206 __set_bit(rotary->down_key, input->keybit); 207 } else { 208 __set_bit(EV_REL, input->evbit); 209 __set_bit(rotary->rel_code, input->relbit); 210 } 211 212 if (rotary->button_key) { 213 __set_bit(EV_KEY, input->evbit); 214 __set_bit(rotary->button_key, input->keybit); 215 } 216 217 /* Quiesce the device before requesting irq */ 218 bfin_rotary_close(input); 219 220 rotary->irq = platform_get_irq(pdev, 0); 221 if (rotary->irq < 0) { 222 dev_err(dev, "No rotary IRQ specified\n"); 223 return -ENOENT; 224 } 225 226 error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr, 227 0, dev_name(dev), rotary); 228 if (error) { 229 dev_err(dev, "unable to claim irq %d; error %d\n", 230 rotary->irq, error); 231 return error; 232 } 233 234 error = input_register_device(input); 235 if (error) { 236 dev_err(dev, "unable to register input device (%d)\n", error); 237 return error; 238 } 239 240 platform_set_drvdata(pdev, rotary); 241 device_init_wakeup(dev, 1); 242 243 return 0; 244} 245 246static int __maybe_unused bfin_rotary_suspend(struct device *dev) 247{ 248 struct platform_device *pdev = to_platform_device(dev); 249 struct bfin_rot *rotary = platform_get_drvdata(pdev); 250 251 rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF); 252 rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF); 253 rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF); 254 255 if (device_may_wakeup(&pdev->dev)) 256 enable_irq_wake(rotary->irq); 257 258 return 0; 259} 260 261static int __maybe_unused bfin_rotary_resume(struct device *dev) 262{ 263 struct platform_device *pdev = to_platform_device(dev); 264 struct bfin_rot *rotary = platform_get_drvdata(pdev); 265 266 writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF); 267 writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF); 268 writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF); 269 270 if (device_may_wakeup(&pdev->dev)) 271 disable_irq_wake(rotary->irq); 272 273 if (rotary->cnt_config & CNTE) 274 writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF); 275 276 return 0; 277} 278 279static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops, 280 bfin_rotary_suspend, bfin_rotary_resume); 281 282static struct platform_driver bfin_rotary_device_driver = { 283 .probe = bfin_rotary_probe, 284 .driver = { 285 .name = "bfin-rotary", 286 .pm = &bfin_rotary_pm_ops, 287 }, 288}; 289module_platform_driver(bfin_rotary_device_driver); 290 291MODULE_LICENSE("GPL"); 292MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 293MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); 294MODULE_ALIAS("platform:bfin-rotary");