Commit | Line | Data |
---|---|---|
01387959 AZ |
1 | /* |
2 | * Generic IXP4xx beeper driver | |
3 | * | |
4 | * Copyright (C) 2005 Tower Technologies | |
5 | * | |
6 | * based on nslu2-io.c | |
7 | * Copyright (C) 2004 Karen Spearel | |
8 | * | |
9 | * Author: Alessandro Zummo <a.zummo@towertech.it> | |
10 | * Maintainers: http://www.nslu2-linux.org/ | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/input.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/platform_device.h> | |
a09d31ff | 22 | #include <linux/interrupt.h> |
e9c9fc23 | 23 | #include <linux/gpio.h> |
a09e64fb | 24 | #include <mach/hardware.h> |
01387959 AZ |
25 | |
26 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | |
27 | MODULE_DESCRIPTION("ixp4xx beeper driver"); | |
28 | MODULE_LICENSE("GPL"); | |
589499c0 | 29 | MODULE_ALIAS("platform:ixp4xx-beeper"); |
01387959 AZ |
30 | |
31 | static DEFINE_SPINLOCK(beep_lock); | |
32 | ||
33 | static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) | |
34 | { | |
35 | unsigned long flags; | |
36 | ||
37 | spin_lock_irqsave(&beep_lock, flags); | |
38 | ||
e9c9fc23 LW |
39 | if (count) { |
40 | gpio_direction_output(pin, 0); | |
01387959 AZ |
41 | *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; |
42 | } else { | |
e9c9fc23 LW |
43 | gpio_direction_output(pin, 1); |
44 | gpio_direction_input(pin); | |
01387959 AZ |
45 | *IXP4XX_OSRT2 = 0; |
46 | } | |
47 | ||
48 | spin_unlock_irqrestore(&beep_lock, flags); | |
49 | } | |
50 | ||
51 | static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | |
52 | { | |
643bd272 | 53 | unsigned int pin = (unsigned int) input_get_drvdata(dev); |
01387959 AZ |
54 | unsigned int count = 0; |
55 | ||
56 | if (type != EV_SND) | |
57 | return -1; | |
58 | ||
59 | switch (code) { | |
60 | case SND_BELL: | |
61 | if (value) | |
62 | value = 1000; | |
63 | case SND_TONE: | |
64 | break; | |
65 | default: | |
66 | return -1; | |
67 | } | |
68 | ||
69 | if (value > 20 && value < 32767) | |
3ae1a6b1 | 70 | count = (ixp4xx_timer_freq / (value * 4)) - 1; |
01387959 AZ |
71 | |
72 | ixp4xx_spkr_control(pin, count); | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
7d12e780 | 77 | static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) |
01387959 | 78 | { |
b22973d0 LW |
79 | unsigned int pin = (unsigned int) dev_id; |
80 | ||
01387959 AZ |
81 | /* clear interrupt */ |
82 | *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND; | |
83 | ||
84 | /* flip the beeper output */ | |
b22973d0 | 85 | gpio_set_value(pin, !gpio_get_value(pin)); |
01387959 AZ |
86 | |
87 | return IRQ_HANDLED; | |
88 | } | |
89 | ||
5298cc4c | 90 | static int ixp4xx_spkr_probe(struct platform_device *dev) |
01387959 AZ |
91 | { |
92 | struct input_dev *input_dev; | |
93 | int err; | |
94 | ||
95 | input_dev = input_allocate_device(); | |
96 | if (!input_dev) | |
97 | return -ENOMEM; | |
98 | ||
373f9713 DT |
99 | input_set_drvdata(input_dev, (void *) dev->id); |
100 | ||
01387959 AZ |
101 | input_dev->name = "ixp4xx beeper", |
102 | input_dev->phys = "ixp4xx/gpio"; | |
103 | input_dev->id.bustype = BUS_HOST; | |
104 | input_dev->id.vendor = 0x001f; | |
105 | input_dev->id.product = 0x0001; | |
106 | input_dev->id.version = 0x0100; | |
293e6392 | 107 | input_dev->dev.parent = &dev->dev; |
01387959 | 108 | |
7b19ada2 JS |
109 | input_dev->evbit[0] = BIT_MASK(EV_SND); |
110 | input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); | |
01387959 AZ |
111 | input_dev->event = ixp4xx_spkr_event; |
112 | ||
b22973d0 LW |
113 | err = gpio_request(dev->id, "ixp4-beeper"); |
114 | if (err) | |
115 | goto err_free_device; | |
116 | ||
01387959 | 117 | err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt, |
ec4665c4 | 118 | IRQF_NO_SUSPEND, "ixp4xx-beeper", |
2dd93203 | 119 | (void *) dev->id); |
01387959 | 120 | if (err) |
b22973d0 | 121 | goto err_free_gpio; |
01387959 AZ |
122 | |
123 | err = input_register_device(input_dev); | |
124 | if (err) | |
125 | goto err_free_irq; | |
126 | ||
127 | platform_set_drvdata(dev, input_dev); | |
128 | ||
129 | return 0; | |
130 | ||
131 | err_free_irq: | |
25c9b7bd | 132 | free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); |
b22973d0 LW |
133 | err_free_gpio: |
134 | gpio_free(dev->id); | |
01387959 AZ |
135 | err_free_device: |
136 | input_free_device(input_dev); | |
137 | ||
138 | return err; | |
139 | } | |
140 | ||
e2619cf7 | 141 | static int ixp4xx_spkr_remove(struct platform_device *dev) |
01387959 AZ |
142 | { |
143 | struct input_dev *input_dev = platform_get_drvdata(dev); | |
373f9713 | 144 | unsigned int pin = (unsigned int) input_get_drvdata(input_dev); |
01387959 AZ |
145 | |
146 | input_unregister_device(input_dev); | |
01387959 AZ |
147 | |
148 | /* turn the speaker off */ | |
149 | disable_irq(IRQ_IXP4XX_TIMER2); | |
150 | ixp4xx_spkr_control(pin, 0); | |
151 | ||
25c9b7bd | 152 | free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); |
b22973d0 | 153 | gpio_free(dev->id); |
01387959 AZ |
154 | |
155 | return 0; | |
156 | } | |
157 | ||
158 | static void ixp4xx_spkr_shutdown(struct platform_device *dev) | |
159 | { | |
160 | struct input_dev *input_dev = platform_get_drvdata(dev); | |
373f9713 | 161 | unsigned int pin = (unsigned int) input_get_drvdata(input_dev); |
01387959 AZ |
162 | |
163 | /* turn off the speaker */ | |
164 | disable_irq(IRQ_IXP4XX_TIMER2); | |
165 | ixp4xx_spkr_control(pin, 0); | |
166 | } | |
167 | ||
168 | static struct platform_driver ixp4xx_spkr_platform_driver = { | |
169 | .driver = { | |
170 | .name = "ixp4xx-beeper", | |
01387959 AZ |
171 | }, |
172 | .probe = ixp4xx_spkr_probe, | |
1cb0aa88 | 173 | .remove = ixp4xx_spkr_remove, |
01387959 AZ |
174 | .shutdown = ixp4xx_spkr_shutdown, |
175 | }; | |
840a746b | 176 | module_platform_driver(ixp4xx_spkr_platform_driver); |
01387959 | 177 |