2 * Rotary counter driver for Analog Devices Blackfin Processors
4 * Copyright 2008-2009 Analog Devices Inc.
5 * Licensed under the GPL-2 or later.
8 #include <linux/module.h>
9 #include <linux/interrupt.h>
11 #include <linux/irq.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>
18 #include <asm/portmux.h>
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 */
30 struct input_dev
*input
;
34 unsigned int down_key
;
35 unsigned int button_key
;
36 unsigned int rel_code
;
39 unsigned short debounce
;
41 unsigned short cnt_config
;
42 unsigned short cnt_imask
;
43 unsigned short cnt_debounce
;
46 static void report_key_event(struct input_dev
*input
, int keycode
)
48 /* simulate a press-n-release */
49 input_report_key(input
, keycode
, 1);
51 input_report_key(input
, keycode
, 0);
55 static void report_rotary_event(struct bfin_rot
*rotary
, int delta
)
57 struct input_dev
*input
= rotary
->input
;
60 report_key_event(input
,
61 delta
> 0 ? rotary
->up_key
: rotary
->down_key
);
63 input_report_rel(input
, rotary
->rel_code
, delta
);
68 static irqreturn_t
bfin_rotary_isr(int irq
, void *dev_id
)
70 struct bfin_rot
*rotary
= dev_id
;
73 switch (readw(rotary
->base
+ CNT_STATUS_OFF
)) {
80 delta
= readl(rotary
->base
+ CNT_COUNTER_OFF
);
82 report_rotary_event(rotary
, delta
);
86 report_key_event(rotary
->input
, rotary
->button_key
);
93 writew(W1LCNT_ZERO
, rotary
->base
+ CNT_COMMAND_OFF
); /* Clear COUNTER */
94 writew(-1, rotary
->base
+ CNT_STATUS_OFF
); /* Clear STATUS */
99 static int bfin_rotary_open(struct input_dev
*input
)
101 struct bfin_rot
*rotary
= input_get_drvdata(input
);
104 if (rotary
->mode
& ROT_DEBE
)
105 writew(rotary
->debounce
& DPRESCALE
,
106 rotary
->base
+ CNT_DEBOUNCE_OFF
);
108 writew(rotary
->mode
& ~CNTE
, rotary
->base
+ CNT_CONFIG_OFF
);
111 if (rotary
->button_key
)
113 writew(val
, rotary
->base
+ CNT_IMASK_OFF
);
115 writew(rotary
->mode
| CNTE
, rotary
->base
+ CNT_CONFIG_OFF
);
120 static void bfin_rotary_close(struct input_dev
*input
)
122 struct bfin_rot
*rotary
= input_get_drvdata(input
);
124 writew(0, rotary
->base
+ CNT_CONFIG_OFF
);
125 writew(0, rotary
->base
+ CNT_IMASK_OFF
);
128 static void bfin_rotary_free_action(void *data
)
130 peripheral_free_list(data
);
133 static int bfin_rotary_probe(struct platform_device
*pdev
)
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
;
142 /* Basic validation */
143 if ((pdata
->rotary_up_key
&& !pdata
->rotary_down_key
) ||
144 (!pdata
->rotary_up_key
&& pdata
->rotary_down_key
)) {
148 if (pdata
->pin_list
) {
149 error
= peripheral_request_list(pdata
->pin_list
,
152 dev_err(dev
, "requesting peripherals failed: %d\n",
157 error
= devm_add_action_or_reset(dev
, bfin_rotary_free_action
,
160 dev_err(dev
, "setting cleanup action failed: %d\n",
166 rotary
= devm_kzalloc(dev
, sizeof(struct bfin_rot
), GFP_KERNEL
);
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
);
175 input
= devm_input_allocate_device(dev
);
179 rotary
->input
= input
;
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
;
186 rotary
->mode
= pdata
->mode
;
187 rotary
->debounce
= pdata
->debounce
;
189 input
->name
= pdev
->name
;
190 input
->phys
= "bfin-rotary/input0";
191 input
->dev
.parent
= dev
;
193 input_set_drvdata(input
, rotary
);
195 input
->id
.bustype
= BUS_HOST
;
196 input
->id
.vendor
= 0x0001;
197 input
->id
.product
= 0x0001;
198 input
->id
.version
= 0x0100;
200 input
->open
= bfin_rotary_open
;
201 input
->close
= bfin_rotary_close
;
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
);
208 __set_bit(EV_REL
, input
->evbit
);
209 __set_bit(rotary
->rel_code
, input
->relbit
);
212 if (rotary
->button_key
) {
213 __set_bit(EV_KEY
, input
->evbit
);
214 __set_bit(rotary
->button_key
, input
->keybit
);
217 /* Quiesce the device before requesting irq */
218 bfin_rotary_close(input
);
220 rotary
->irq
= platform_get_irq(pdev
, 0);
221 if (rotary
->irq
< 0) {
222 dev_err(dev
, "No rotary IRQ specified\n");
226 error
= devm_request_irq(dev
, rotary
->irq
, bfin_rotary_isr
,
227 0, dev_name(dev
), rotary
);
229 dev_err(dev
, "unable to claim irq %d; error %d\n",
234 error
= input_register_device(input
);
236 dev_err(dev
, "unable to register input device (%d)\n", error
);
240 platform_set_drvdata(pdev
, rotary
);
241 device_init_wakeup(dev
, 1);
246 static int __maybe_unused
bfin_rotary_suspend(struct device
*dev
)
248 struct platform_device
*pdev
= to_platform_device(dev
);
249 struct bfin_rot
*rotary
= platform_get_drvdata(pdev
);
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
);
255 if (device_may_wakeup(&pdev
->dev
))
256 enable_irq_wake(rotary
->irq
);
261 static int __maybe_unused
bfin_rotary_resume(struct device
*dev
)
263 struct platform_device
*pdev
= to_platform_device(dev
);
264 struct bfin_rot
*rotary
= platform_get_drvdata(pdev
);
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
);
270 if (device_may_wakeup(&pdev
->dev
))
271 disable_irq_wake(rotary
->irq
);
273 if (rotary
->cnt_config
& CNTE
)
274 writew(rotary
->cnt_config
, rotary
->base
+ CNT_CONFIG_OFF
);
279 static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops
,
280 bfin_rotary_suspend
, bfin_rotary_resume
);
282 static struct platform_driver bfin_rotary_device_driver
= {
283 .probe
= bfin_rotary_probe
,
285 .name
= "bfin-rotary",
286 .pm
= &bfin_rotary_pm_ops
,
289 module_platform_driver(bfin_rotary_device_driver
);
291 MODULE_LICENSE("GPL");
292 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
293 MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
294 MODULE_ALIAS("platform:bfin-rotary");