disable IMQ on 2.6.28 as well -- people should use IFB..
[openwrt.git] / target / linux / s3c24xx / patches / 0031-lis302dl.patch.patch
1 From 65c5d85b4cf89969d2e2e981c018bf0ef1c03a2a Mon Sep 17 00:00:00 2001
2 From: mokopatches <mokopatches@openmoko.org>
3 Date: Wed, 16 Jul 2008 14:46:56 +0100
4 Subject: [PATCH] lis302dl.patch
5  This is a Linux driver for the STmicro LIS302DL 3-axis accelerometer.
6
7 Signed-off-by: Harald Welte <laforge@openmoko.org>
8 ---
9  arch/arm/mach-s3c2440/mach-gta02.c |   46 +++-
10  drivers/input/misc/Kconfig         |    9 +
11  drivers/input/misc/Makefile        |    2 +
12  drivers/input/misc/lis302dl.c      |  633 ++++++++++++++++++++++++++++++++++++
13  include/linux/lis302dl.h           |   11 +
14  5 files changed, 700 insertions(+), 1 deletions(-)
15  create mode 100644 drivers/input/misc/lis302dl.c
16  create mode 100644 include/linux/lis302dl.h
17
18 diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
19 index f72a5ae..d11da10 100644
20 --- a/arch/arm/mach-s3c2440/mach-gta02.c
21 +++ b/arch/arm/mach-s3c2440/mach-gta02.c
22 @@ -46,6 +46,7 @@
23  #include <linux/mtd/physmap.h>
24  
25  #include <linux/pcf50633.h>
26 +#include <linux/lis302dl.h>
27  
28  #include <asm/mach/arch.h>
29  #include <asm/mach/map.h>
30 @@ -463,7 +464,7 @@ static struct s3c2410_ts_mach_info gta02_ts_cfg = {
31         .oversampling_shift = 5,
32  };
33  
34 -/* SPI */
35 +/* SPI: LCM control interface attached to Glamo3362 */
36  
37  static struct spi_board_info gta02_spi_board_info[] = {
38         {
39 @@ -504,6 +505,48 @@ static struct platform_device gta01_led_dev = {
40         .resource       = gta01_led_resources,
41  };
42  
43 +/* SPI: Accelerometers attached to SPI of s3c244x */
44 +
45 +static void gta02_spi_acc_set_cs(struct s3c2410_spi_info *spi, int cs, int pol)
46 +{
47 +       s3c2410_gpio_setpin(cs, pol);
48 +}
49 +
50 +static const struct lis302dl_platform_data lis302_pdata[] = {
51 +       {
52 +               .name           = "lis302-1 (top)"
53 +       }, {
54 +               .name           = "lis302-2 (bottom)"
55 +       },
56 +};
57 +
58 +static struct spi_board_info gta02_spi_acc_bdinfo[] = {
59 +       {
60 +               .modalias       = "lis302dl",
61 +               .platform_data  = &lis302_pdata[0],
62 +               .irq            = GTA02_IRQ_GSENSOR_1,
63 +               .max_speed_hz   = 400 * 1000,
64 +               .bus_num        = 1,
65 +               .chip_select    = S3C2410_GPD12,
66 +               .mode           = SPI_MODE_3,
67 +       },
68 +       {
69 +               .modalias       = "lis302dl",
70 +               .platform_data  = &lis302_pdata[1],
71 +               .irq            = GTA02_IRQ_GSENSOR_2,
72 +               .max_speed_hz   = 400 * 1000,
73 +               .bus_num        = 1,
74 +               .chip_select    = S3C2410_GPD13,
75 +               .mode           = SPI_MODE_3,
76 +       },
77 +};
78 +
79 +static struct s3c2410_spi_info gta02_spi_acc_cfg = {
80 +       .set_cs         = gta02_spi_acc_set_cs,
81 +       .board_size     = ARRAY_SIZE(gta02_spi_acc_bdinfo),
82 +       .board_info     = gta02_spi_acc_bdinfo,
83 +};
84 +
85  static struct resource gta02_led_resources[] = {
86         {
87                 .name   = "gta02-power:orange",
88 @@ -746,6 +789,7 @@ static void __init gta02_machine_init(void)
89         s3c_device_usb.dev.platform_data = &gta02_usb_info;
90         s3c_device_nand.dev.platform_data = &gta02_nand_info;
91         s3c_device_sdi.dev.platform_data = &gta02_mmc_cfg;
92 +       s3c_device_spi1.dev.platform_data = &gta02_spi_acc_cfg;
93  
94         /* Only GTA02v1 has a SD_DETECT GPIO.  Since the slot is not
95          * hot-pluggable, this is not required anyway */
96 diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
97 index 432699d..ac8bcf4 100644
98 --- a/drivers/input/misc/Kconfig
99 +++ b/drivers/input/misc/Kconfig
100 @@ -197,4 +197,13 @@ config HP_SDC_RTC
101           Say Y here if you want to support the built-in real time clock
102           of the HP SDC controller.
103  
104 +config INPUT_LIS302DL
105 +       tristate "STmicro LIS302DL 3-axis accelerometer"
106 +       depends on SPI_MASTER
107 +       help
108 +         SPI driver for the STmicro LIS302DL 3-axis accelerometer.
109 +
110 +         The userspece interface is a 3-axis (X/Y/Z) relative movement
111 +         Linux input device, reporting REL_[XYZ] events.
112 +
113  endif
114 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
115 index ebd39f2..0d223ba 100644
116 --- a/drivers/input/misc/Makefile
117 +++ b/drivers/input/misc/Makefile
118 @@ -20,3 +20,4 @@
119  obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
120  obj-$(CONFIG_INPUT_APANEL)             += apanel.o
121  obj-$(CONFIG_INPUT_GPIO_BUTTONS)       += gpio_buttons.o
122 +obj-$(CONFIG_INPUT_LIS302DL)           += lis302dl.o
123 diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
124 new file mode 100644
125 index 0000000..45c41c8
126 --- /dev/null
127 +++ b/drivers/input/misc/lis302dl.c
128 @@ -0,0 +1,633 @@
129 +/* Linux kernel driver for the ST LIS302D 3-axis accelerometer
130 + *
131 + * Copyright (C) 2007 by OpenMoko, Inc.
132 + * Author: Harald Welte <laforge@openmoko.org>
133 + * All rights reserved.
134 + *
135 + * This program is free software; you can redistribute it and/or
136 + * modify it under the terms of the GNU General Public License as
137 + * published by the Free Software Foundation; either version 2 of
138 + * the License, or (at your option) any later version.
139 + *
140 + * This program is distributed in the hope that it will be useful,
141 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
142 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
143 + * GNU General Public License for more details.
144 + *
145 + * You should have received a copy of the GNU General Public License
146 + * along with this program; if not, write to the Free Software
147 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
148 + * MA 02111-1307 USA
149 + *
150 + * TODO
151 + *     * statistics for overflow events
152 + *     * configuration interface (sysfs) for
153 + *             * enable/disable x/y/z axis data ready
154 + *             * enable/disable resume from freee fall / click
155 + *             * free fall / click parameters
156 + *             * high pass filter parameters
157 + */
158 +#include <linux/kernel.h>
159 +#include <linux/types.h>
160 +#include <linux/module.h>
161 +#include <linux/device.h>
162 +#include <linux/platform_device.h>
163 +#include <linux/delay.h>
164 +#include <linux/input.h>
165 +#include <linux/irq.h>
166 +#include <linux/interrupt.h>
167 +#include <linux/sysfs.h>
168 +
169 +#include <linux/lis302dl.h>
170 +
171 +#include <linux/spi/spi.h>
172 +
173 +#define LIS302DL_WHO_AM_I_MAGIC                0x3b
174 +
175 +enum lis302dl_reg {
176 +       LIS302DL_REG_WHO_AM_I           = 0x0f,
177 +       LIS302DL_REG_CTRL1              = 0x20,
178 +       LIS302DL_REG_CTRL2              = 0x21,
179 +       LIS302DL_REG_CTRL3              = 0x22,
180 +       LIS302DL_REG_HP_FILTER_RESET    = 0x23,
181 +       LIS302DL_REG_STATUS             = 0x27,
182 +       LIS302DL_REG_OUT_X              = 0x29,
183 +       LIS302DL_REG_OUT_Y              = 0x2b,
184 +       LIS302DL_REG_OUT_Z              = 0x2d,
185 +       LIS302DL_REG_FF_WU_CFG_1        = 0x30,
186 +       LIS302DL_REG_FF_WU_SRC_1        = 0x31,
187 +       LIS302DL_REG_FF_WU_THS_1        = 0x32,
188 +       LIS302DL_REG_FF_WU_DURATION_1   = 0x33,
189 +       LIS302DL_REG_FF_WU_CFG_2        = 0x34,
190 +       LIS302DL_REG_FF_WU_SRC_2        = 0x35,
191 +       LIS302DL_REG_FF_WU_THS_2        = 0x36,
192 +       LIS302DL_REG_FF_WU_DURATION_2   = 0x37,
193 +       LIS302DL_REG_CLICK_CFG          = 0x38,
194 +       LIS302DL_REG_CLICK_SRC          = 0x39,
195 +       LIS302DL_REG_CLICK_THSY_X       = 0x3b,
196 +       LIS302DL_REG_CLICK_THSZ         = 0x3c,
197 +       LIS302DL_REG_CLICK_TIME_LIMIT   = 0x3d,
198 +       LIS302DL_REG_CLICK_LATENCY      = 0x3e,
199 +       LIS302DL_REG_CLICK_WINDOW       = 0x3f,
200 +};
201 +
202 +enum lis302dl_reg_ctrl1 {
203 +       LIS302DL_CTRL1_Xen              = 0x01,
204 +       LIS302DL_CTRL1_Yen              = 0x02,
205 +       LIS302DL_CTRL1_Zen              = 0x04,
206 +       LIS302DL_CTRL1_STM              = 0x08,
207 +       LIS302DL_CTRL1_STP              = 0x10,
208 +       LIS302DL_CTRL1_FS               = 0x20,
209 +       LIS302DL_CTRL1_PD               = 0x40,
210 +       LIS302DL_CTRL1_DR               = 0x80,
211 +};
212 +
213 +enum lis302dl_reg_ctrl3 {
214 +       LIS302DL_CTRL3_PP_OD            = 0x40,
215 +};
216 +
217 +enum lis302dl_reg_status {
218 +       LIS302DL_STATUS_XDA             = 0x01,
219 +       LIS302DL_STATUS_YDA             = 0x02,
220 +       LIS302DL_STATUS_ZDA             = 0x04,
221 +       LIS302DL_STATUS_XYZDA           = 0x08,
222 +       LIS302DL_STATUS_XOR             = 0x10,
223 +       LIS302DL_STATUS_YOR             = 0x20,
224 +       LIS302DL_STATUS_ZOR             = 0x40,
225 +       LIS302DL_STATUS_XYZOR           = 0x80,
226 +};
227 +
228 +enum lis302dl_reg_ffwusrc1 {
229 +       LIS302DL_FFWUSRC1_XL            = 0x01,
230 +       LIS302DL_FFWUSRC1_XH            = 0x02,
231 +       LIS302DL_FFWUSRC1_YL            = 0x04,
232 +       LIS302DL_FFWUSRC1_YH            = 0x08,
233 +       LIS302DL_FFWUSRC1_ZL            = 0x10,
234 +       LIS302DL_FFWUSRC1_ZH            = 0x20,
235 +       LIS302DL_FFWUSRC1_IA            = 0x40,
236 +};
237 +
238 +enum lis302dl_reg_cloik_src {
239 +       LIS302DL_CLICKSRC_SINGLE_X      = 0x01,
240 +       LIS302DL_CLICKSRC_DOUBLE_X      = 0x02,
241 +       LIS302DL_CLICKSRC_SINGLE_Y      = 0x04,
242 +       LIS302DL_CLICKSRC_DOUBLE_Y      = 0x08,
243 +       LIS302DL_CLICKSRC_SINGLE_Z      = 0x10,
244 +       LIS302DL_CLICKSRC_DOUBLE_Z      = 0x20,
245 +       LIS302DL_CLICKSRC_IA            = 0x40,
246 +};
247 +
248 +struct lis302dl_info {
249 +       struct spi_device *spi_dev;
250 +       struct input_dev *input_dev;
251 +       struct mutex lock;
252 +       struct work_struct work;
253 +       unsigned int flags;
254 +       unsigned int working;
255 +       u_int8_t regs[0x40];
256 +};
257 +
258 +#define LIS302DL_F_WUP_FF              0x0001  /* wake up from free fall */
259 +#define LIS302DL_F_WUP_CLICK           0x0002
260 +#define LIS302DL_F_POWER               0x0010
261 +#define LIS302DL_F_FS                  0x0020  /* ADC full scale */
262 +
263 +/* lowlevel register access functions */
264 +
265 +#define READ_BIT       0x01
266 +#define MS_BIT         0x02
267 +#define ADDR_SHIFT     2
268 +
269 +static inline u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg)
270 +{
271 +       int rc;
272 +       u_int8_t cmd;
273 +
274 +       cmd = (reg << ADDR_SHIFT) | READ_BIT;
275 +
276 +       rc = spi_w8r8(lis->spi_dev, cmd);
277 +
278 +       return rc;
279 +}
280 +
281 +static u_int8_t reg_read(struct lis302dl_info *lis, u_int8_t reg)
282 +{
283 +       u_int8_t ret;
284 +
285 +       mutex_lock(&lis->lock);
286 +       ret = __reg_read(lis, reg);
287 +       mutex_unlock(&lis->lock);
288 +
289 +       return ret;
290 +}
291 +
292 +static inline int __reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val)
293 +{
294 +       u_int8_t buf[2];
295 +
296 +       buf[0] = (reg << ADDR_SHIFT);
297 +       buf[1] = val;
298 +
299 +       return spi_write(lis->spi_dev, buf, sizeof(buf));
300 +}
301 +
302 +static int reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val)
303 +{
304 +       int ret;
305 +
306 +       mutex_lock(&lis->lock);
307 +       ret = __reg_write(lis, reg, val);
308 +       mutex_unlock(&lis->lock);
309 +
310 +       return ret;
311 +}
312 +
313 +static int reg_set_bit_mask(struct lis302dl_info *lis,
314 +                           u_int8_t reg, u_int8_t mask, u_int8_t val)
315 +{
316 +       int ret;
317 +       u_int8_t tmp;
318 +
319 +       val &= mask;
320 +
321 +       mutex_lock(&lis->lock);
322 +
323 +       tmp = __reg_read(lis, reg);
324 +       tmp &= ~mask;
325 +       tmp |= val;
326 +       ret = __reg_write(lis, reg, tmp);
327 +
328 +       mutex_unlock(&lis->lock);
329 +
330 +       return ret;
331 +}
332 +
333 +/* interrupt handling related */
334 +
335 +enum lis302dl_intmode {
336 +       LIS302DL_INTMODE_GND            = 0x00,
337 +       LIS302DL_INTMODE_FF_WU_1        = 0x01,
338 +       LIX302DL_INTMODE_FF_WU_2        = 0x02,
339 +       LIX302DL_INTMODE_FF_WU_12       = 0x03,
340 +       LIX302DL_INTMODE_DATA_READY     = 0x04,
341 +       LIX302DL_INTMODE_CLICK          = 0x07,
342 +};
343 +
344 +static void lis302dl_int_mode(struct spi_device *spi, int int_pin,
345 +                             enum lis302dl_intmode mode)
346 +{
347 +       struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
348 +
349 +       if (int_pin == 1)
350 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode);
351 +       else if (int_pin == 2)
352 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3);
353 +}
354 +
355 +static void _report_btn_single(struct input_dev *inp, int btn)
356 +{
357 +       input_report_key(inp, btn, 1);
358 +       input_sync(inp);
359 +       input_report_key(inp, btn, 0);
360 +}
361 +
362 +static void _report_btn_double(struct input_dev *inp, int btn)
363 +{
364 +       input_report_key(inp, btn, 1);
365 +       input_sync(inp);
366 +       input_report_key(inp, btn, 0);
367 +       input_sync(inp);
368 +       input_report_key(inp, btn, 1);
369 +       input_sync(inp);
370 +       input_report_key(inp, btn, 0);
371 +}
372 +
373 +static void lis302dl_work(struct work_struct *work)
374 +{
375 +       struct lis302dl_info *lis =
376 +                       container_of(work, struct lis302dl_info, work);
377 +
378 +       u_int8_t status, ff_wu_src_1, click_src;
379 +       u_int8_t val;
380 +
381 +       lis->working = 1;
382 +
383 +       status = reg_read(lis, LIS302DL_REG_STATUS);
384 +       ff_wu_src_1 = reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
385 +       click_src = reg_read(lis, LIS302DL_REG_CLICK_SRC);
386 +
387 +       if (status & LIS302DL_STATUS_XDA) {
388 +               val = reg_read(lis, LIS302DL_REG_OUT_X);
389 +               if (lis->flags & LIS302DL_F_FS)
390 +                       val = val << 2;
391 +               input_report_rel(lis->input_dev, REL_X, val);
392 +       }
393 +
394 +       if (status & LIS302DL_STATUS_YDA) {
395 +               val = reg_read(lis, LIS302DL_REG_OUT_Y);
396 +               if (lis->flags & LIS302DL_F_FS)
397 +                       val = val << 2;
398 +               input_report_rel(lis->input_dev, REL_Y, val);
399 +       }
400 +
401 +       if (status & LIS302DL_STATUS_ZDA) {
402 +               val = reg_read(lis, LIS302DL_REG_OUT_Z);
403 +               if (lis->flags & LIS302DL_F_FS)
404 +                       val = val << 2;
405 +               input_report_rel(lis->input_dev, REL_Z, val);
406 +       }
407 +
408 +       if (status & 0xf0)
409 +               dev_dbg(&lis->spi_dev->dev, "overrun!\n");
410 +
411 +       /* FIXME: implement overrun statistics */
412 +
413 +       if (ff_wu_src_1 & LIS302DL_FFWUSRC1_IA) {
414 +               /* FIXME: free fall interrupt handling */
415 +       }
416 +
417 +       if (click_src & LIS302DL_CLICKSRC_IA) {
418 +               if (click_src & LIS302DL_CLICKSRC_SINGLE_X)
419 +                       _report_btn_single(lis->input_dev, BTN_X);
420 +               if (click_src & LIS302DL_CLICKSRC_DOUBLE_X)
421 +                       _report_btn_double(lis->input_dev, BTN_X);
422 +
423 +               if (click_src & LIS302DL_CLICKSRC_SINGLE_Y)
424 +                       _report_btn_single(lis->input_dev, BTN_Y);
425 +               if (click_src & LIS302DL_CLICKSRC_DOUBLE_Y)
426 +                       _report_btn_double(lis->input_dev, BTN_Y);
427 +
428 +               if (click_src & LIS302DL_CLICKSRC_SINGLE_Z)
429 +                       _report_btn_single(lis->input_dev, BTN_Z);
430 +               if (click_src & LIS302DL_CLICKSRC_DOUBLE_Z)
431 +                       _report_btn_double(lis->input_dev, BTN_Z);
432 +       }
433 +
434 +       lis->working = 0;
435 +       input_sync(lis->input_dev);
436 +       put_device(&lis->spi_dev->dev);
437 +
438 +       enable_irq(lis->spi_dev->irq);
439 +}
440 +
441 +static void lis302dl_schedule_work(struct lis302dl_info *lis)
442 +{
443 +       int status;
444 +
445 +       get_device(&lis->spi_dev->dev);
446 +       status = schedule_work(&lis->work);
447 +       if (!status && !lis->working)
448 +               dev_dbg(&lis->spi_dev->dev, "work item may be lost\n");
449 +}
450 +
451 +static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
452 +{
453 +       struct lis302dl_info *lis = _lis;
454 +
455 +       lis302dl_schedule_work(lis);
456 +
457 +       /* Disable any further interrupts until we have processed
458 +        * the current one */
459 +       disable_irq(lis->spi_dev->irq);
460 +
461 +       return IRQ_HANDLED;
462 +}
463 +
464 +/* sysfs */
465 +
466 +static ssize_t show_rate(struct device *dev, struct device_attribute *attr,
467 +                        char *buf)
468 +{
469 +       struct lis302dl_info *lis = dev_get_drvdata(dev);
470 +       u_int8_t ctrl1 = reg_read(lis, LIS302DL_REG_CTRL1);
471 +
472 +       return sprintf(buf, "%d\n", ctrl1 & LIS302DL_CTRL1_DR ? 400 : 100);
473 +}
474 +
475 +static ssize_t set_rate(struct device *dev, struct device_attribute *attr,
476 +                       const char *buf, size_t count)
477 +{
478 +       struct lis302dl_info *lis = dev_get_drvdata(dev);
479 +
480 +       if (!strcmp(buf, "400\n"))
481 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR,
482 +                                LIS302DL_CTRL1_DR);
483 +       else
484 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, 0);
485 +
486 +       return count;
487 +}
488 +
489 +static DEVICE_ATTR(sample_rate, S_IRUGO | S_IWUSR, show_rate, set_rate);
490 +
491 +static ssize_t show_scale(struct device *dev, struct device_attribute *attr,
492 +                         char *buf)
493 +{
494 +       struct lis302dl_info *lis = dev_get_drvdata(dev);
495 +       u_int8_t ctrl1 = reg_read(lis, LIS302DL_REG_CTRL1);
496 +
497 +       return sprintf(buf, "%s\n", ctrl1 & LIS302DL_CTRL1_FS ? "9.2" : "2.3");
498 +}
499 +
500 +static ssize_t set_scale(struct device *dev, struct device_attribute *attr,
501 +                        const char *buf, size_t count)
502 +{
503 +       struct lis302dl_info *lis = dev_get_drvdata(dev);
504 +
505 +       if (!strcmp(buf, "9.2\n"))
506 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS,
507 +                                LIS302DL_CTRL1_FS);
508 +       else
509 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, 0);
510 +
511 +       return count;
512 +}
513 +
514 +static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale);
515 +
516 +static struct attribute *lis302dl_sysfs_entries[] = {
517 +       &dev_attr_sample_rate.attr,
518 +       &dev_attr_full_scale.attr,
519 +};
520 +
521 +static struct attribute_group lis302dl_attr_group = {
522 +       .name   = NULL,
523 +       .attrs  = lis302dl_sysfs_entries,
524 +};
525 +
526 +/* input device handling and driver core interaction */
527 +
528 +static int lis302dl_input_open(struct input_dev *inp)
529 +{
530 +       struct lis302dl_info *lis = inp->private;
531 +       u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen |
532 +                        LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
533 +
534 +       /* make sure we're powered up and generate data ready */
535 +       reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
536 +
537 +       return 0;
538 +}
539 +
540 +static void lis302dl_input_close(struct input_dev *inp)
541 +{
542 +       struct lis302dl_info *lis = inp->private;
543 +       u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen |
544 +                        LIS302DL_CTRL1_Zen;
545 +
546 +       /* since the input core already serializes access and makes sure we
547 +        * only see close() for the close of the lastre user, we can safely
548 +        * disable the data ready events */
549 +       reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, 0x00);
550 +
551 +       /* however, don't power down the whole device if still needed */
552 +       if (!(lis->flags & LIS302DL_F_WUP_FF ||
553 +             lis->flags & LIS302DL_F_WUP_CLICK)) {
554 +               reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD,
555 +                                0x00);
556 +       }
557 +}
558 +
559 +static int __devinit lis302dl_probe(struct spi_device *spi)
560 +{
561 +       int rc;
562 +       struct lis302dl_info *lis;
563 +       u_int8_t wai;
564 +
565 +       lis = kzalloc(sizeof(*lis), GFP_KERNEL);
566 +       if (!lis)
567 +               return -ENOMEM;
568 +
569 +       mutex_init(&lis->lock);
570 +       INIT_WORK(&lis->work, lis302dl_work);
571 +       lis->spi_dev = spi;
572 +
573 +       spi_set_drvdata(spi, lis);
574 +
575 +       rc = spi_setup(spi);
576 +       if (rc < 0) {
577 +               printk(KERN_ERR "error durign spi_setup of lis302dl driver\n");
578 +               dev_set_drvdata(&spi->dev, NULL);
579 +               kfree(lis);
580 +               return rc;
581 +       }
582 +
583 +       wai = reg_read(lis, LIS302DL_REG_WHO_AM_I);
584 +       if (wai != LIS302DL_WHO_AM_I_MAGIC) {
585 +               printk(KERN_ERR "unknown who_am_i signature 0x%02x\n", wai);
586 +               dev_set_drvdata(&spi->dev, NULL);
587 +               kfree(lis);
588 +               return -ENODEV;
589 +       }
590 +
591 +       /* switch interrupt to open collector */
592 +       reg_write(lis, LIS302DL_CTRL3_PP_OD, 0x7c);
593 +
594 +       rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt, IRQF_DISABLED,
595 +                        "lis302dl", NULL);
596 +       if (rc < 0) {
597 +               dev_err(&spi->dev, "error requesting IRQ %d\n",
598 +                       lis->spi_dev->irq);
599 +               /* FIXME */
600 +               return rc;
601 +       }
602 +
603 +       rc = sysfs_create_group(&spi->dev.kobj, &lis302dl_attr_group);
604 +       if (rc) {
605 +               dev_err(&spi->dev, "error creating sysfs group\n");
606 +               /* FIXME */
607 +               return rc;
608 +       }
609 +
610 +       /* initialize input layer details */
611 +       lis->input_dev = input_allocate_device();
612 +       if (!lis->input_dev) {
613 +               dev_err(&spi->dev, "Unable to allocate input device\n");
614 +               /* FIXME */
615 +       }
616 +
617 +       set_bit(EV_REL, lis->input_dev->evbit);
618 +       set_bit(EV_KEY, lis->input_dev->evbit);
619 +       set_bit(BTN_X, lis->input_dev->keybit);
620 +       set_bit(BTN_Y, lis->input_dev->keybit);
621 +       set_bit(BTN_Z, lis->input_dev->keybit);
622 +
623 +       lis->input_dev->private = lis;
624 +       lis->input_dev->name = "lis302dl"; /* FIXME: platform data */
625 +       lis->input_dev->id.bustype = BUS_I2C; /* FIXME: SPI Bus */
626 +       lis->input_dev->open = lis302dl_input_open;
627 +       lis->input_dev->close = lis302dl_input_close;
628 +
629 +       input_register_device(lis->input_dev);
630 +
631 +       return 0;
632 +}
633 +
634 +static int __devexit lis302dl_remove(struct spi_device *spi)
635 +{
636 +       struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
637 +
638 +       /* power down the device */
639 +       reg_write(lis, LIS302DL_REG_CTRL1, 0x00);
640 +       sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group);
641 +       input_unregister_device(lis->input_dev);
642 +       dev_set_drvdata(&spi->dev, NULL);
643 +       kfree(lis);
644 +
645 +       return 0;
646 +}
647 +
648 +#ifdef CONFIG_PM
649 +static int lis302dl_suspend(struct spi_device *spi, pm_message_t state)
650 +{
651 +       struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
652 +
653 +       /* save registers */
654 +       lis->regs[LIS302DL_REG_CTRL1] = reg_read(lis, LIS302DL_REG_CTRL1);
655 +       lis->regs[LIS302DL_REG_CTRL2] = reg_read(lis, LIS302DL_REG_CTRL2);
656 +       lis->regs[LIS302DL_REG_CTRL3] = reg_read(lis, LIS302DL_REG_CTRL3);
657 +       lis->regs[LIS302DL_REG_FF_WU_CFG_1] =
658 +                               reg_read(lis, LIS302DL_REG_FF_WU_CFG_1);
659 +       lis->regs[LIS302DL_REG_FF_WU_THS_1] =
660 +                               reg_read(lis, LIS302DL_REG_FF_WU_THS_1);
661 +       lis->regs[LIS302DL_REG_FF_WU_DURATION_1] =
662 +                               reg_read(lis, LIS302DL_REG_FF_WU_DURATION_1);
663 +       lis->regs[LIS302DL_REG_FF_WU_CFG_2] =
664 +                               reg_read(lis, LIS302DL_REG_FF_WU_CFG_2);
665 +       lis->regs[LIS302DL_REG_FF_WU_THS_2] =
666 +                               reg_read(lis, LIS302DL_REG_FF_WU_THS_2);
667 +       lis->regs[LIS302DL_REG_FF_WU_DURATION_2] =
668 +                               reg_read(lis, LIS302DL_REG_FF_WU_DURATION_2);
669 +       lis->regs[LIS302DL_REG_CLICK_CFG] =
670 +                               reg_read(lis, LIS302DL_REG_CLICK_CFG);
671 +       lis->regs[LIS302DL_REG_CLICK_THSY_X] =
672 +                               reg_read(lis, LIS302DL_REG_CLICK_THSY_X);
673 +       lis->regs[LIS302DL_REG_CLICK_THSZ] =
674 +                               reg_read(lis, LIS302DL_REG_CLICK_THSZ);
675 +       lis->regs[LIS302DL_REG_CLICK_TIME_LIMIT] =
676 +                               reg_read(lis, LIS302DL_REG_CLICK_TIME_LIMIT);
677 +       lis->regs[LIS302DL_REG_CLICK_LATENCY] =
678 +                               reg_read(lis, LIS302DL_REG_CLICK_LATENCY);
679 +       lis->regs[LIS302DL_REG_CLICK_WINDOW] =
680 +                               reg_read(lis, LIS302DL_REG_CLICK_WINDOW);
681 +
682 +       /* determine if we want to wake up from the accel. */
683 +       if (!(lis->flags & LIS302DL_F_WUP_FF ||
684 +             lis->flags & LIS302DL_F_WUP_CLICK)) {
685 +               /* power down */
686 +               u_int8_t tmp;
687 +               tmp = reg_read(lis, LIS302DL_REG_CTRL1);
688 +               tmp &= ~LIS302DL_CTRL1_PD;
689 +               reg_write(lis, LIS302DL_REG_CTRL1, tmp);
690 +       }
691 +
692 +       return 0;
693 +}
694 +
695 +static int lis302dl_resume(struct spi_device *spi)
696 +{
697 +       struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
698 +
699 +       /* restore registers after resume */
700 +       reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1]);
701 +       reg_write(lis, LIS302DL_REG_CTRL2, lis->regs[LIS302DL_REG_CTRL2]);
702 +       reg_write(lis, LIS302DL_REG_CTRL3, lis->regs[LIS302DL_REG_CTRL3]);
703 +       reg_write(lis, LIS302DL_REG_FF_WU_CFG_1,
704 +                 lis->regs[LIS302DL_REG_FF_WU_CFG_1]);
705 +       reg_write(lis, LIS302DL_REG_FF_WU_THS_1,
706 +                 lis->regs[LIS302DL_REG_FF_WU_THS_1]);
707 +       reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
708 +                 lis->regs[LIS302DL_REG_FF_WU_DURATION_1]);
709 +       reg_write(lis, LIS302DL_REG_FF_WU_CFG_2,
710 +                 lis->regs[LIS302DL_REG_FF_WU_CFG_2]);
711 +       reg_write(lis, LIS302DL_REG_FF_WU_THS_2,
712 +                 lis->regs[LIS302DL_REG_FF_WU_THS_2]);
713 +       reg_write(lis, LIS302DL_REG_FF_WU_DURATION_2,
714 +                 lis->regs[LIS302DL_REG_FF_WU_DURATION_2]);
715 +       reg_write(lis, LIS302DL_REG_CLICK_CFG,
716 +                 lis->regs[LIS302DL_REG_CLICK_CFG]);
717 +       reg_write(lis, LIS302DL_REG_CLICK_THSY_X,
718 +                 lis->regs[LIS302DL_REG_CLICK_THSY_X]);
719 +       reg_write(lis, LIS302DL_REG_CLICK_THSZ,
720 +                 lis->regs[LIS302DL_REG_CLICK_THSZ]);
721 +       reg_write(lis, LIS302DL_REG_CLICK_TIME_LIMIT,
722 +                 lis->regs[LIS302DL_REG_CLICK_TIME_LIMIT]);
723 +       reg_write(lis, LIS302DL_REG_CLICK_LATENCY,
724 +                 lis->regs[LIS302DL_REG_CLICK_LATENCY]);
725 +       reg_write(lis, LIS302DL_REG_CLICK_WINDOW,
726 +                 lis->regs[LIS302DL_REG_CLICK_WINDOW]);
727 +
728 +       return 0;
729 +}
730 +#else
731 +#define lis302dl_suspend       NULL
732 +#define lis302dl_resume                NULL
733 +#endif
734 +
735 +static struct spi_driver lis302dl_driver = {
736 +       .driver = {
737 +               .name   = "lis302dl",
738 +               .owner  = THIS_MODULE,
739 +       },
740 +
741 +       .probe   = lis302dl_probe,
742 +       .remove  = __devexit_p(lis302dl_remove),
743 +       .suspend = lis302dl_suspend,
744 +       .resume  = lis302dl_resume,
745 +};
746 +
747 +static int __init lis302dl_init(void)
748 +{
749 +       return spi_register_driver(&lis302dl_driver);
750 +}
751 +
752 +static void __exit lis302dl_exit(void)
753 +{
754 +       spi_unregister_driver(&lis302dl_driver);
755 +}
756 +
757 +MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
758 +MODULE_LICENSE("GPL");
759 +
760 +module_init(lis302dl_init);
761 +module_exit(lis302dl_exit);
762 diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
763 new file mode 100644
764 index 0000000..d0f31be
765 --- /dev/null
766 +++ b/include/linux/lis302dl.h
767 @@ -0,0 +1,11 @@
768 +#ifndef _LINUX_LIS302DL_H
769 +#define _LINUX_LIS302DL_H
770 +
771 +#include <linux/types.h>
772 +
773 +struct lis302dl_platform_data {
774 +       char *name;
775 +};
776 +
777 +#endif /* _LINUX_LIS302DL_H */
778 +
779 -- 
780 1.5.6.3
781