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