]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge current mainline tree into linux-omap tree
authorTony Lindgren <tony@atomide.com>
Fri, 3 Apr 2009 00:10:56 +0000 (17:10 -0700)
committerTony Lindgren <tony@atomide.com>
Fri, 3 Apr 2009 00:10:56 +0000 (17:10 -0700)
Merge branches 'master' and 'linus'

Conflicts:
arch/arm/configs/omap_3430sdp_defconfig
arch/arm/configs/rx51_defconfig
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-apollon.c
arch/arm/mach-omap2/board-h4.c
arch/arm/mach-omap2/board-ldp.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/board-rx51.c
arch/arm/mach-omap2/mmc-twl4030.c
arch/arm/mach-omap2/mmc-twl4030.h
arch/arm/mach-omap2/usb-musb.c
arch/arm/plat-omap/dma.c
arch/arm/plat-omap/include/mach/board-nokia.h
arch/arm/plat-omap/include/mach/irqs.h
arch/arm/plat-omap/include/mach/usb.h
drivers/Makefile
drivers/i2c/chips/Kconfig
drivers/video/omap/omapfb_main.c
net/ipv4/netfilter/Makefile

41 files changed:
1  2 
Makefile
arch/arm/Kconfig
arch/arm/include/asm/setup.h
arch/arm/mach-omap1/Kconfig
arch/arm/mach-omap2/Kconfig
arch/arm/plat-omap/gpio.c
arch/arm/plat-omap/include/mach/irqs.h
arch/arm/plat-omap/sram.c
drivers/Makefile
drivers/char/hw_random/omap-rng.c
drivers/crypto/Kconfig
drivers/crypto/Makefile
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/i2c/busses/i2c-omap.c
drivers/i2c/chips/Kconfig
drivers/i2c/chips/Makefile
drivers/input/keyboard/Kconfig
drivers/input/keyboard/lm8323.c
drivers/input/keyboard/tsc2301_kp.c
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/tsc2301_ts.c
drivers/leds/Kconfig
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/nand/Kconfig
drivers/mtd/nand/omap-nand-flash.c
drivers/mtd/nand/omap2.c
drivers/net/smc91x.c
drivers/spi/omap2_mcspi.c
drivers/usb/Kconfig
drivers/usb/host/Kconfig
drivers/usb/host/ehci-hcd.c
drivers/video/omap/omapfb_main.c
include/linux/connector.h
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/Makefile

diff --cc Makefile
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index d12c39fd76c022913bca32c1b3ed30ffdc09310e,7f57ee66f364dd0ad65092f58d62066c95a3604d..9499a0520e0f5554de292d4cef2e757fd0b4d22e
  #define INT_34XX_MMC3_IRQ     94
  #define INT_34XX_GPT12_IRQ    95
  
- /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and
 -#define       INT_34XX_BENCH_MPU_EMUL 3
 -
+ /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730/850) and
   * 16 MPUIO lines */
  #define OMAP_MAX_GPIO_LINES   192
  #define IH_GPIO_BASE          (128 + IH2_BASE)
Simple merge
index fec4d8e2170719a51b03be06640664e70c955928,2618a6169a1304379a8644725f0663aee3ac7fd9..3db0a27a914a64cfcc1dffa3040b47af2946fca3
@@@ -34,16 -34,10 +34,15 @@@ obj-$(CONFIG_CONNECTOR)            += connector
  obj-$(CONFIG_FB_I810)           += video/i810/
  obj-$(CONFIG_FB_INTEL)          += video/intelfb/
  
 +# we also need input/serio early so serio bus is initialized by the time
 +# serial drivers start registering their serio ports
 +obj-$(CONFIG_SERIO)           += input/serio/
  obj-y                         += serial/
  obj-$(CONFIG_PARPORT)         += parport/
- obj-y                         += base/ block/ misc/ mfd/ net/ media/
+ obj-y                         += base/ block/ misc/ mfd/ media/
 +obj-y                         += i2c/
 +obj-y                         += cbus/
  obj-$(CONFIG_NUBUS)           += nubus/
- obj-$(CONFIG_ATM)             += atm/
  obj-y                         += macintosh/
  obj-$(CONFIG_IDE)             += ide/
  obj-$(CONFIG_SCSI)            += scsi/
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index bf7bd1b9109e2b03d8bb3d268908ddbbe548b513,8f8c81eb0aee750006a7949ed530d10a4257503c..a01f40778ae61d57bd3c61cb0122e57d8c58ce39
@@@ -64,44 -64,6 +64,31 @@@ config SENSORS_PCA953
          This driver is deprecated and will be dropped soon. Use
          drivers/gpio/pca953x.c instead.
  
- config SENSORS_PCF8591
-       tristate "Philips PCF8591"
-       depends on EXPERIMENTAL
-       default n
-       help
-         If you say yes here you get support for Philips PCF8591 chips.
-         This driver can also be built as a module.  If so, the module
-         will be called pcf8591.
-         These devices are hard to detect and rarely found on mainstream
-         hardware.  If unsure, say N.
 +config TWL4030_MADC
 +      tristate "TWL4030 MADC Driver"
 +      depends on TWL4030_CORE
 +      help
 +        The TWL4030 Monitoring ADC driver enables the host
 +        processor to monitor analog signals using analog-to-digital
 +        conversions on the input source. TWL4030 MADC provides the
 +        following features:
 +         - Single 10-bit ADC with successive approximation register (SAR) conversion;
 +         - Analog multiplexer for 16 inputs;
 +         - Seven (of the 16) inputs are freely available;
 +         - Battery voltage monitoring;
 +         - Concurrent conversion request management;
 +         - Interrupt signal to Primary Interrupt Handler;
 +         - Averaging feature;
 +         - Selective enable/disable of the averaging feature.
 +
 +        Say 'y' here to statically link this module into the kernel or 'm'
 +        to build it as a dinamically loadable module. The module will be
 +        called twl4030-madc.ko
 +
 +config TWL4030_POWEROFF
 +      tristate "TWL4030 device poweroff"
 +      depends on TWL4030_CORE
 +
  config SENSORS_MAX6875
        tristate "Maxim MAX6875 Power supply supervisor"
        depends on EXPERIMENTAL
index 8da23eeebe581d8e8ce81df797b16d0d20d7f549,55a37603718332dcceab0ef5c90f38a6cb054a8e..0f8a24539a9854a6d3534c40f7074283a2ad7c62
@@@ -15,11 -15,7 +15,10 @@@ obj-$(CONFIG_SENSORS_MAX6875)       += max687
  obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
  obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
  obj-$(CONFIG_PCF8575)         += pcf8575.o
- obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
  obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
 +obj-$(CONFIG_TWL4030_POWEROFF)        += twl4030-poweroff.o
 +obj-$(CONFIG_TWL4030_MADC)    += twl4030-madc.o
 +obj-$(CONFIG_RTC_X1205_I2C)   += x1205.o
  
  ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
  EXTRA_CFLAGS += -DDEBUG
Simple merge
index a796680b785ac87e786d719d0f6d803f53081f29,0000000000000000000000000000000000000000..a95ea0333007ec2d2a14fed7ee600e8ac83e408b
mode 100644,000000..100644
--- /dev/null
@@@ -1,928 -1,0 +1,928 @@@
-       snprintf(lm->phys, sizeof(lm->phys), "%s/input-kp", client->dev.bus_id);
 +/*
 + * drivers/i2c/chips/lm8323.c
 + *
 + * Copyright (C) 2007-2009 Nokia Corporation
 + *
 + * Written by Daniel Stone <daniel.stone@nokia.com>
 + *            Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
 + *
 + * Updated by Felipe Balbi <felipe.balbi@nokia.com>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation (version 2 of the License only).
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + */
 +
 +#include <linux/module.h>
 +#include <linux/i2c.h>
 +#include <linux/interrupt.h>
 +#include <linux/sched.h>
 +#include <linux/mutex.h>
 +#include <linux/delay.h>
 +#include <linux/input.h>
 +#include <linux/leds.h>
 +#include <linux/i2c/lm8323.h>
 +
 +/* Commands to send to the chip. */
 +#define LM8323_CMD_READ_ID            0x80 /* Read chip ID. */
 +#define LM8323_CMD_WRITE_CFG          0x81 /* Set configuration item. */
 +#define LM8323_CMD_READ_INT           0x82 /* Get interrupt status. */
 +#define LM8323_CMD_RESET              0x83 /* Reset, same as external one */
 +#define LM8323_CMD_WRITE_PORT_SEL     0x85 /* Set GPIO in/out. */
 +#define LM8323_CMD_WRITE_PORT_STATE   0x86 /* Set GPIO pullup. */
 +#define LM8323_CMD_READ_PORT_SEL      0x87 /* Get GPIO in/out. */
 +#define LM8323_CMD_READ_PORT_STATE    0x88 /* Get GPIO pullup. */
 +#define LM8323_CMD_READ_FIFO          0x89 /* Read byte from FIFO. */
 +#define LM8323_CMD_RPT_READ_FIFO      0x8a /* Read FIFO (no increment). */
 +#define LM8323_CMD_SET_ACTIVE         0x8b /* Set active time. */
 +#define LM8323_CMD_READ_ERR           0x8c /* Get error status. */
 +#define LM8323_CMD_READ_ROTATOR               0x8e /* Read rotator status. */
 +#define LM8323_CMD_SET_DEBOUNCE               0x8f /* Set debouncing time. */
 +#define LM8323_CMD_SET_KEY_SIZE               0x90 /* Set keypad size. */
 +#define LM8323_CMD_READ_KEY_SIZE      0x91 /* Get keypad size. */
 +#define LM8323_CMD_READ_CFG           0x92 /* Get configuration item. */
 +#define LM8323_CMD_WRITE_CLOCK                0x93 /* Set clock config. */
 +#define LM8323_CMD_READ_CLOCK         0x94 /* Get clock config. */
 +#define LM8323_CMD_PWM_WRITE          0x95 /* Write PWM script. */
 +#define LM8323_CMD_START_PWM          0x96 /* Start PWM engine. */
 +#define LM8323_CMD_STOP_PWM           0x97 /* Stop PWM engine. */
 +
 +/* Interrupt status. */
 +#define INT_KEYPAD                    0x01 /* Key event. */
 +#define INT_ROTATOR                   0x02 /* Rotator event. */
 +#define INT_ERROR                     0x08 /* Error: use CMD_READ_ERR. */
 +#define INT_NOINIT                    0x10 /* Lost configuration. */
 +#define INT_PWM1                      0x20 /* PWM1 stopped. */
 +#define INT_PWM2                      0x40 /* PWM2 stopped. */
 +#define INT_PWM3                      0x80 /* PWM3 stopped. */
 +
 +/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */
 +#define ERR_BADPAR                    0x01 /* Bad parameter. */
 +#define ERR_CMDUNK                    0x02 /* Unknown command. */
 +#define ERR_KEYOVR                    0x04 /* Too many keys pressed. */
 +#define ERR_FIFOOVER                  0x40 /* FIFO overflow. */
 +
 +/* Configuration keys (CMD_{WRITE,READ}_CFG). */
 +#define CFG_MUX1SEL                   0x01 /* Select MUX1_OUT input. */
 +#define CFG_MUX1EN                    0x02 /* Enable MUX1_OUT. */
 +#define CFG_MUX2SEL                   0x04 /* Select MUX2_OUT input. */
 +#define CFG_MUX2EN                    0x08 /* Enable MUX2_OUT. */
 +#define CFG_PSIZE                     0x20 /* Package size (must be 0). */
 +#define CFG_ROTEN                     0x40 /* Enable rotator. */
 +
 +/* Clock settings (CMD_{WRITE,READ}_CLOCK). */
 +#define CLK_RCPWM_INTERNAL            0x00
 +#define CLK_RCPWM_EXTERNAL            0x03
 +#define CLK_SLOWCLKEN                 0x08 /* Enable 32.768kHz clock. */
 +#define CLK_SLOWCLKOUT                        0x40 /* Enable slow pulse output. */
 +
 +/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */
 +#define LM8323_I2C_ADDR00             (0x84 >> 1)     /* 1000 010x */
 +#define LM8323_I2C_ADDR01             (0x86 >> 1)     /* 1000 011x */
 +#define LM8323_I2C_ADDR10             (0x88 >> 1)     /* 1000 100x */
 +#define LM8323_I2C_ADDR11             (0x8A >> 1)     /* 1000 101x */
 +
 +/* Key event fifo length */
 +#define LM8323_FIFO_LEN                       15
 +
 +/* Commands for PWM engine; feed in with PWM_WRITE. */
 +/* Load ramp counter from duty cycle field (range 0 - 0xff). */
 +#define PWM_SET(v)                    (0x4000 | ((v) & 0xff))
 +/* Go to start of script. */
 +#define PWM_GOTOSTART                 0x0000
 +/*
 + * Stop engine (generates interrupt).  If reset is 1, clear the program
 + * counter, else leave it.
 + */
 +#define PWM_END(reset)                        (0xc000 | (!!(reset) << 11))
 +/*
 + * Ramp.  If s is 1, divide clock by 512, else divide clock by 16.
 + * Take t clock scales (up to 63) per step, for n steps (up to 126).
 + * If u is set, ramp up, else ramp down.
 + */
 +#define PWM_RAMP(s, t, n, u)          ((!!(s) << 14) | ((t) & 0x3f) << 8 | \
 +                                       ((n) & 0x7f) | ((u) ? 0 : 0x80))
 +/*
 + * Loop (i.e. jump back to pos) for a given number of iterations (up to 63).
 + * If cnt is zero, execute until PWM_END is encountered.
 + */
 +#define PWM_LOOP(cnt, pos)            (0xa000 | (((cnt) & 0x3f) << 7) | \
 +                                       ((pos) & 0x3f))
 +/*
 + * Wait for trigger.  Argument is a mask of channels, shifted by the channel
 + * number, e.g. 0xa for channels 3 and 1.  Note that channels are numbered
 + * from 1, not 0.
 + */
 +#define PWM_WAIT_TRIG(chans)          (0xe000 | (((chans) & 0x7) << 6))
 +/* Send trigger.  Argument is same as PWM_WAIT_TRIG. */
 +#define PWM_SEND_TRIG(chans)          (0xe000 | ((chans) & 0x7))
 +
 +struct lm8323_pwm {
 +      int                     id;
 +      int                     enabled;
 +      int                     fade_time;
 +      int                     brightness;
 +      int                     desired_brightness;
 +      int                     running;
 +      /* pwm lock */
 +      struct mutex            lock;
 +      struct work_struct      work;
 +      struct led_classdev     cdev;
 +};
 +
 +struct lm8323_chip {
 +      /* device lock */
 +      struct mutex            lock;
 +      struct i2c_client       *client;
 +      struct work_struct      work;
 +      struct input_dev        *idev;
 +      unsigned                kp_enabled:1;
 +      unsigned                pm_suspend:1;
 +      unsigned                keys_down;
 +      char                    phys[32];
 +      s16                     keymap[LM8323_KEYMAP_SIZE];
 +      int                     size_x;
 +      int                     size_y;
 +      int                     debounce_time;
 +      int                     active_time;
 +      struct lm8323_pwm       pwm1;
 +      struct lm8323_pwm       pwm2;
 +      struct lm8323_pwm       pwm3;
 +};
 +
 +#define client_to_lm8323(c)   container_of(c, struct lm8323_chip, client)
 +#define dev_to_lm8323(d)      container_of(d, struct lm8323_chip, client->dev)
 +#define work_to_lm8323(w)     container_of(w, struct lm8323_chip, work)
 +#define cdev_to_pwm(c)                container_of(c, struct lm8323_pwm, cdev)
 +#define work_to_pwm(w)                container_of(w, struct lm8323_pwm, work)
 +
 +static struct lm8323_chip *pwm_to_lm8323(struct lm8323_pwm *pwm)
 +{
 +      switch (pwm->id) {
 +      case 1:
 +              return container_of(pwm, struct lm8323_chip, pwm1);
 +      case 2:
 +              return container_of(pwm, struct lm8323_chip, pwm2);
 +      case 3:
 +              return container_of(pwm, struct lm8323_chip, pwm3);
 +      default:
 +              return NULL;
 +      }
 +}
 +
 +#define LM8323_MAX_DATA 8
 +
 +/*
 + * To write, we just access the chip's address in write mode, and dump the
 + * command and data out on the bus.  The command byte and data are taken as
 + * sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA.
 + */
 +static int lm8323_write(struct lm8323_chip *lm, int len, ...)
 +{
 +      int ret, i;
 +      va_list ap;
 +      u8 data[LM8323_MAX_DATA];
 +
 +      va_start(ap, len);
 +
 +      if (unlikely(len > LM8323_MAX_DATA)) {
 +              dev_err(&lm->client->dev, "tried to send %d bytes\n", len);
 +              va_end(ap);
 +              return 0;
 +      }
 +
 +      for (i = 0; i < len; i++)
 +              data[i] = va_arg(ap, int);
 +
 +      va_end(ap);
 +
 +      /*
 +       * If the host is asleep while we send the data, we can get a NACK
 +       * back while it wakes up, so try again, once.
 +       */
 +      ret = i2c_master_send(lm->client, data, len);
 +      if (unlikely(ret == -EREMOTEIO))
 +              ret = i2c_master_send(lm->client, data, len);
 +      if (unlikely(ret != len))
 +              dev_err(&lm->client->dev, "sent %d bytes of %d total\n",
 +                      len, ret);
 +
 +      return ret;
 +}
 +
 +/*
 + * To read, we first send the command byte to the chip and end the transaction,
 + * then access the chip in read mode, at which point it will send the data.
 + */
 +static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len)
 +{
 +      int ret;
 +
 +      /*
 +       * If the host is asleep while we send the byte, we can get a NACK
 +       * back while it wakes up, so try again, once.
 +       */
 +      ret = i2c_master_send(lm->client, &cmd, 1);
 +      if (unlikely(ret == -EREMOTEIO))
 +              ret = i2c_master_send(lm->client, &cmd, 1);
 +      if (unlikely(ret != 1)) {
 +              dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n",
 +                      cmd);
 +              return 0;
 +      }
 +
 +      ret = i2c_master_recv(lm->client, buf, len);
 +      if (unlikely(ret != len))
 +              dev_err(&lm->client->dev, "wanted %d bytes, got %d\n",
 +                      len, ret);
 +
 +      return ret;
 +}
 +
 +/*
 + * Set the chip active time (idle time before it enters halt).
 + */
 +static void lm8323_set_active_time(struct lm8323_chip *lm, int time)
 +{
 +      lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2);
 +}
 +
 +/*
 + * The signals are AT-style: the low 7 bits are the keycode, and the top
 + * bit indicates the state (1 for down, 0 for up).
 + */
 +static inline u8 lm8323_whichkey(u8 event)
 +{
 +      return event & 0x7f;
 +}
 +
 +static inline int lm8323_ispress(u8 event)
 +{
 +      return (event & 0x80) ? 1 : 0;
 +}
 +
 +static void process_keys(struct lm8323_chip *lm)
 +{
 +      u8 event;
 +      u8 key_fifo[LM8323_FIFO_LEN + 1];
 +      int old_keys_down = lm->keys_down;
 +      int ret;
 +      int i = 0;
 +
 +      /*
 +       * Read all key events from the FIFO at once. Next READ_FIFO clears the
 +       * FIFO even if we didn't read all events previously.
 +       */
 +      ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN);
 +
 +      if (ret < 0) {
 +              dev_err(&lm->client->dev, "Failed reading fifo \n");
 +              return;
 +      }
 +      key_fifo[ret] = 0;
 +
 +      while ((event = key_fifo[i])) {
 +              u8 key = lm8323_whichkey(event);
 +              int isdown = lm8323_ispress(event);
 +              s16 keycode = lm->keymap[key];
 +
 +              if (likely(keycode > 0)) {
 +                      dev_vdbg(&lm->client->dev, "key 0x%02x %s\n", key,
 +                            isdown ? "down" : "up");
 +                      if (likely(lm->kp_enabled)) {
 +                              input_report_key(lm->idev, keycode, isdown);
 +                              input_sync(lm->idev);
 +                      }
 +                      if (isdown)
 +                              lm->keys_down++;
 +                      else
 +                              lm->keys_down--;
 +              } else {
 +                      dev_err(&lm->client->dev, "keycode 0x%02x not mapped "
 +                              "to any key\n", key);
 +              }
 +              i++;
 +      }
 +
 +      /*
 +       * Errata: We need to ensure that the chip never enters halt mode
 +       * during a keypress, so set active time to 0.  When it's released,
 +       * we can enter halt again, so set the active time back to normal.
 +       */
 +      if (!old_keys_down && lm->keys_down)
 +              lm8323_set_active_time(lm, 0);
 +      if (old_keys_down && !lm->keys_down)
 +              lm8323_set_active_time(lm, lm->active_time);
 +}
 +
 +static void lm8323_process_error(struct lm8323_chip *lm)
 +{
 +      u8 error;
 +
 +      if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) {
 +              if (error & ERR_FIFOOVER)
 +                      dev_vdbg(&lm->client->dev, "fifo overflow!\n");
 +              if (error & ERR_KEYOVR)
 +                      dev_vdbg(&lm->client->dev,
 +                                      "more than two keys pressed\n");
 +              if (error & ERR_CMDUNK)
 +                      dev_vdbg(&lm->client->dev,
 +                                      "unknown command submitted\n");
 +              if (error & ERR_BADPAR)
 +                      dev_vdbg(&lm->client->dev, "bad command parameter\n");
 +      }
 +}
 +
 +static void lm8323_reset(struct lm8323_chip *lm)
 +{
 +      /* The docs say we must pass 0xAA as the data byte. */
 +      lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA);
 +}
 +
 +static int lm8323_configure(struct lm8323_chip *lm)
 +{
 +      int keysize = (lm->size_x << 4) | lm->size_y;
 +      int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL);
 +      int debounce = lm->debounce_time >> 2;
 +      int active = lm->active_time >> 2;
 +
 +      /*
 +       * Active time must be greater than the debounce time: if it's
 +       * a close-run thing, give ourselves a 12ms buffer.
 +       */
 +      if (debounce >= active)
 +              active = debounce + 3;
 +
 +      lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0);
 +      lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock);
 +      lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize);
 +      lm8323_set_active_time(lm, lm->active_time);
 +      lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce);
 +      lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff);
 +      lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0);
 +
 +      /*
 +       * Not much we can do about errors at this point, so just hope
 +       * for the best.
 +       */
 +
 +      return 0;
 +}
 +
 +static void pwm_done(struct lm8323_pwm *pwm)
 +{
 +      mutex_lock(&pwm->lock);
 +      pwm->running = 0;
 +      if (pwm->desired_brightness != pwm->brightness)
 +              schedule_work(&pwm->work);
 +      mutex_unlock(&pwm->lock);
 +}
 +
 +/*
 + * Bottom half: handle the interrupt by posting key events, or dealing with
 + * errors appropriately.
 + */
 +static void lm8323_work(struct work_struct *work)
 +{
 +      struct lm8323_chip *lm = work_to_lm8323(work);
 +      u8 ints;
 +
 +      mutex_lock(&lm->lock);
 +
 +      while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) {
 +              if (likely(ints & INT_KEYPAD))
 +                      process_keys(lm);
 +              if (ints & INT_ROTATOR) {
 +                      /* We don't currently support the rotator. */
 +                      dev_vdbg(&lm->client->dev, "rotator fired\n");
 +              }
 +              if (ints & INT_ERROR) {
 +                      dev_vdbg(&lm->client->dev, "error!\n");
 +                      lm8323_process_error(lm);
 +              }
 +              if (ints & INT_NOINIT) {
 +                      dev_err(&lm->client->dev, "chip lost config; "
 +                                                "reinitialising\n");
 +                      lm8323_configure(lm);
 +              }
 +              if (ints & INT_PWM1) {
 +                      dev_vdbg(&lm->client->dev, "pwm1 engine completed\n");
 +                      pwm_done(&lm->pwm1);
 +              }
 +              if (ints & INT_PWM2) {
 +                      dev_vdbg(&lm->client->dev, "pwm2 engine completed\n");
 +                      pwm_done(&lm->pwm2);
 +              }
 +              if (ints & INT_PWM3) {
 +                      dev_vdbg(&lm->client->dev, "pwm3 engine completed\n");
 +                      pwm_done(&lm->pwm3);
 +              }
 +      }
 +
 +      mutex_unlock(&lm->lock);
 +}
 +
 +/*
 + * We cannot use I2C in interrupt context, so we just schedule work.
 + */
 +static irqreturn_t lm8323_irq(int irq, void *data)
 +{
 +      struct lm8323_chip *lm = data;
 +
 +      schedule_work(&lm->work);
 +
 +      return IRQ_HANDLED;
 +}
 +
 +/*
 + * Read the chip ID.
 + */
 +static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf)
 +{
 +      int bytes;
 +
 +      bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2);
 +      if (unlikely(bytes != 2))
 +              return -EIO;
 +
 +      return 0;
 +}
 +
 +static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd)
 +{
 +      struct lm8323_chip *lm = pwm_to_lm8323(pwm);
 +
 +      lm8323_write(lm, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id,
 +                   (cmd & 0xff00) >> 8, cmd & 0x00ff);
 +}
 +
 +/*
 + * Write a script into a given PWM engine, concluding with PWM_END.
 + * If 'kill' is nonzero, the engine will be shut down at the end
 + * of the script, producing a zero output. Otherwise the engine
 + * will be kept running at the final PWM level indefinitely.
 + */
 +static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
 +                           int len, const u16 *cmds)
 +{
 +      struct lm8323_chip *lm = pwm_to_lm8323(pwm);
 +      int i;
 +
 +      for (i = 0; i < len; i++)
 +              lm8323_write_pwm_one(pwm, i, cmds[i]);
 +
 +      lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
 +      lm8323_write(lm, 2, LM8323_CMD_START_PWM, pwm->id);
 +      pwm->running = 1;
 +}
 +
 +static void lm8323_pwm_work(struct work_struct *work)
 +{
 +      struct lm8323_pwm *pwm = work_to_pwm(work);
 +      int div512, perstep, steps, hz, up, kill;
 +      u16 pwm_cmds[3];
 +      int num_cmds = 0;
 +
 +      mutex_lock(&pwm->lock);
 +
 +      /*
 +       * Do nothing if we're already at the requested level,
 +       * or previous setting is not yet complete. In the latter
 +       * case we will be called again when the previous PWM script
 +       * finishes.
 +       */
 +      if (pwm->running || pwm->desired_brightness == pwm->brightness) {
 +              mutex_unlock(&pwm->lock);
 +              return;
 +      }
 +
 +      kill = (pwm->desired_brightness == 0);
 +      up = (pwm->desired_brightness > pwm->brightness);
 +      steps = abs(pwm->desired_brightness - pwm->brightness);
 +
 +      /*
 +       * Convert time (in ms) into a divisor (512 or 16 on a refclk of
 +       * 32768Hz), and number of ticks per step.
 +       */
 +      if ((pwm->fade_time / steps) > (32768 / 512)) {
 +              div512 = 1;
 +              hz = 32768 / 512;
 +      } else {
 +              div512 = 0;
 +              hz = 32768 / 16;
 +      }
 +
 +      perstep = (hz * pwm->fade_time) / (steps * 1000);
 +
 +      if (perstep == 0)
 +              perstep = 1;
 +      else if (perstep > 63)
 +              perstep = 63;
 +
 +      while (steps) {
 +              int s;
 +
 +              s = min(126, steps);
 +              pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
 +              steps -= s;
 +      }
 +
 +      lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
 +
 +      pwm->brightness = pwm->desired_brightness;
 +      mutex_unlock(&pwm->lock);
 +}
 +
 +static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
 +                                    enum led_brightness brightness)
 +{
 +      struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
 +      struct lm8323_chip *lm = pwm_to_lm8323(pwm);
 +
 +      mutex_lock(&pwm->lock);
 +      pwm->desired_brightness = brightness;
 +      mutex_unlock(&pwm->lock);
 +
 +      if (in_interrupt()) {
 +              schedule_work(&pwm->work);
 +      } else {
 +              /*
 +               * Schedule PWM work as usual unless we are going into suspend
 +               */
 +              mutex_lock(&lm->lock);
 +              if (likely(!lm->pm_suspend))
 +                      schedule_work(&pwm->work);
 +              else
 +                      lm8323_pwm_work(&pwm->work);
 +              mutex_unlock(&lm->lock);
 +      }
 +}
 +
 +static ssize_t lm8323_pwm_show_time(struct device *dev,
 +              struct device_attribute *attr, char *buf)
 +{
 +      struct led_classdev *led_cdev = dev_get_drvdata(dev);
 +      struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
 +
 +      return sprintf(buf, "%d\n", pwm->fade_time);
 +}
 +
 +static ssize_t lm8323_pwm_store_time(struct device *dev,
 +              struct device_attribute *attr, const char *buf, size_t len)
 +{
 +      struct led_classdev *led_cdev = dev_get_drvdata(dev);
 +      struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
 +      int ret;
 +      int time;
 +
 +      ret = strict_strtoul(buf, 10, &time);
 +      /* Numbers only, please. */
 +      if (ret)
 +              return -EINVAL;
 +
 +      pwm->fade_time = time;
 +
 +      return strlen(buf);
 +}
 +static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time);
 +
 +static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev,
 +                  const char *name)
 +{
 +      struct lm8323_pwm *pwm = NULL;
 +
 +      BUG_ON(id > 3);
 +
 +      switch (id) {
 +      case 1:
 +              pwm = &lm->pwm1;
 +              break;
 +      case 2:
 +              pwm = &lm->pwm2;
 +              break;
 +      case 3:
 +              pwm = &lm->pwm3;
 +              break;
 +      }
 +
 +      pwm->id = id;
 +      pwm->fade_time = 0;
 +      pwm->brightness = 0;
 +      pwm->desired_brightness = 0;
 +      pwm->running = 0;
 +      mutex_init(&pwm->lock);
 +      if (name) {
 +              pwm->cdev.name = name;
 +              pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
 +              if (led_classdev_register(dev, &pwm->cdev) < 0) {
 +                      dev_err(dev, "couldn't register PWM %d\n", id);
 +                      return -1;
 +              }
 +              if (device_create_file(pwm->cdev.dev,
 +                                           &dev_attr_time) < 0) {
 +                      dev_err(dev, "couldn't register time attribute\n");
 +                      led_classdev_unregister(&pwm->cdev);
 +                      return -1;
 +              }
 +              INIT_WORK(&pwm->work, lm8323_pwm_work);
 +              pwm->enabled = 1;
 +      } else {
 +              pwm->enabled = 0;
 +      }
 +
 +      return 0;
 +}
 +
 +static struct i2c_driver lm8323_i2c_driver;
 +
 +static ssize_t lm8323_show_disable(struct device *dev,
 +                                 struct device_attribute *attr, char *buf)
 +{
 +      struct lm8323_chip *lm = dev_get_drvdata(dev);
 +
 +      return sprintf(buf, "%u\n", !lm->kp_enabled);
 +}
 +
 +static ssize_t lm8323_set_disable(struct device *dev,
 +                                struct device_attribute *attr,
 +                                const char *buf, size_t count)
 +{
 +      struct lm8323_chip *lm = dev_get_drvdata(dev);
 +      int ret;
 +      int i;
 +
 +      ret = strict_strtoul(buf, 10, &i);
 +
 +      mutex_lock(&lm->lock);
 +      lm->kp_enabled = !i;
 +      mutex_unlock(&lm->lock);
 +
 +      return count;
 +}
 +static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
 +
 +static int lm8323_probe(struct i2c_client *client,
 +              const struct i2c_device_id *id)
 +{
 +      struct lm8323_platform_data *pdata;
 +      struct input_dev *idev;
 +      struct lm8323_chip *lm;
 +      int i, err = 0;
 +      unsigned long tmo;
 +      u8 data[2];
 +
 +      lm = kzalloc(sizeof *lm, GFP_KERNEL);
 +      if (!lm)
 +              return -ENOMEM;
 +
 +      i2c_set_clientdata(client, lm);
 +      lm->client = client;
 +      pdata = client->dev.platform_data;
 +      if (!pdata || !pdata->size_x || !pdata->size_y) {
 +              dev_err(&client->dev, "missing platform_data\n");
 +              err = -EINVAL;
 +              goto fail2;
 +      }
 +
 +      lm->size_x = pdata->size_x;
 +      if (lm->size_x > 8) {
 +              dev_err(&client->dev, "invalid x size %d specified\n",
 +                              lm->size_x);
 +              err = -EINVAL;
 +              goto fail2;
 +      }
 +
 +      lm->size_y = pdata->size_y;
 +      if (lm->size_y > 12) {
 +              dev_err(&client->dev, "invalid y size %d specified\n",
 +                              lm->size_y);
 +              err = -EINVAL;
 +              goto fail2;
 +      }
 +
 +      dev_vdbg(&client->dev, "Keypad size: %d x %d\n",
 +                      lm->size_x, lm->size_y);
 +
 +      lm->debounce_time = pdata->debounce_time;
 +      lm->active_time = pdata->active_time;
 +
 +      lm8323_reset(lm);
 +
 +      /* Nothing's set up to service the IRQ yet, so just spin for max.
 +       * 100ms until we can configure. */
 +      tmo = jiffies + msecs_to_jiffies(100);
 +      while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) {
 +              if (data[0] & INT_NOINIT)
 +                      break;
 +
 +              if (time_after(jiffies, tmo)) {
 +                      dev_err(&client->dev,
 +                                      "timeout waiting for initialisation\n");
 +                      break;
 +              }
 +
 +              msleep(1);
 +      }
 +      lm8323_configure(lm);
 +
 +      /* If a true probe check the device */
 +      if (lm8323_read_id(lm, data) != 0) {
 +              dev_err(&client->dev, "device not found\n");
 +              err = -ENODEV;
 +              goto fail2;
 +      }
 +
 +      if (init_pwm(lm, 1, &client->dev, pdata->pwm1_name) < 0)
 +              goto fail3;
 +      if (init_pwm(lm, 2, &client->dev, pdata->pwm2_name) < 0)
 +              goto fail4;
 +      if (init_pwm(lm, 3, &client->dev, pdata->pwm3_name) < 0)
 +              goto fail5;
 +
 +      mutex_init(&lm->lock);
 +      INIT_WORK(&lm->work, lm8323_work);
 +
 +      err = request_irq(client->irq, lm8323_irq,
 +                        IRQF_TRIGGER_FALLING | IRQF_DISABLED,
 +                        "lm8323", lm);
 +      if (err) {
 +              dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
 +              goto fail6;
 +      }
 +
 +      device_init_wakeup(&client->dev, 1);
 +      enable_irq_wake(client->irq);
 +
 +      lm->kp_enabled = 1;
 +      err = device_create_file(&client->dev, &dev_attr_disable_kp);
 +      if (err < 0)
 +              goto fail7;
 +
 +      idev = input_allocate_device();
 +      if (!idev) {
 +              err = -ENOMEM;
 +              goto fail8;
 +      }
 +
 +      if (pdata->name)
 +              idev->name = pdata->name;
 +      else
 +              idev->name = "LM8323 keypad";
++      snprintf(lm->phys, sizeof(lm->phys), "%s/input-kp", dev_name(&client->dev));
 +      idev->phys = lm->phys;
 +
 +      lm->keys_down = 0;
 +      idev->evbit[0] = BIT(EV_KEY);
 +      for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
 +              if (pdata->keymap[i] > 0)
 +                      __set_bit(pdata->keymap[i], idev->keybit);
 +
 +              lm->keymap[i] = pdata->keymap[i];
 +      }
 +
 +      if (pdata->repeat)
 +              __set_bit(EV_REP, idev->evbit);
 +
 +      lm->idev = idev;
 +      err = input_register_device(idev);
 +      if (err) {
 +              dev_dbg(&client->dev, "error registering input device\n");
 +              goto fail8;
 +      }
 +
 +      return 0;
 +
 +fail8:
 +      device_remove_file(&client->dev, &dev_attr_disable_kp);
 +fail7:
 +      free_irq(client->irq, lm);
 +fail6:
 +      if (lm->pwm3.enabled)
 +              led_classdev_unregister(&lm->pwm3.cdev);
 +fail5:
 +      if (lm->pwm2.enabled)
 +              led_classdev_unregister(&lm->pwm2.cdev);
 +fail4:
 +      if (lm->pwm1.enabled)
 +              led_classdev_unregister(&lm->pwm1.cdev);
 +fail3:
 +fail2:
 +      kfree(lm);
 +      return err;
 +}
 +
 +static int lm8323_remove(struct i2c_client *client)
 +{
 +      struct lm8323_chip *lm = i2c_get_clientdata(client);
 +
 +      disable_irq_wake(client->irq);
 +      free_irq(client->irq, lm);
 +      cancel_work_sync(&lm->work);
 +      input_unregister_device(lm->idev);
 +      device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
 +      if (lm->pwm3.enabled)
 +              led_classdev_unregister(&lm->pwm3.cdev);
 +      if (lm->pwm2.enabled)
 +              led_classdev_unregister(&lm->pwm2.cdev);
 +      if (lm->pwm1.enabled)
 +              led_classdev_unregister(&lm->pwm1.cdev);
 +      kfree(lm);
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM
 +/*
 + * We don't need to explicitly suspend the chip, as it already switches off
 + * when there's no activity.
 + */
 +static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg)
 +{
 +      struct lm8323_chip *lm = i2c_get_clientdata(client);
 +
 +      set_irq_wake(client->irq, 0);
 +      disable_irq(client->irq);
 +
 +      mutex_lock(&lm->lock);
 +      lm->pm_suspend = 1;
 +      mutex_unlock(&lm->lock);
 +
 +      if (lm->pwm1.enabled)
 +              led_classdev_suspend(&lm->pwm1.cdev);
 +      if (lm->pwm2.enabled)
 +              led_classdev_suspend(&lm->pwm2.cdev);
 +      if (lm->pwm3.enabled)
 +              led_classdev_suspend(&lm->pwm3.cdev);
 +
 +      return 0;
 +}
 +
 +static int lm8323_resume(struct i2c_client *client)
 +{
 +      struct lm8323_chip *lm = i2c_get_clientdata(client);
 +
 +      mutex_lock(&lm->lock);
 +      lm->pm_suspend = 0;
 +      mutex_unlock(&lm->lock);
 +
 +      if (lm->pwm1.enabled)
 +              led_classdev_resume(&lm->pwm1.cdev);
 +      if (lm->pwm2.enabled)
 +              led_classdev_resume(&lm->pwm2.cdev);
 +      if (lm->pwm3.enabled)
 +              led_classdev_resume(&lm->pwm3.cdev);
 +
 +      enable_irq(client->irq);
 +      set_irq_wake(client->irq, 1);
 +
 +      return 0;
 +}
 +#else
 +#define lm8323_suspend        NULL
 +#define lm8323_resume NULL
 +#endif
 +
 +static const struct i2c_device_id lm8323_id[] = {
 +      { "lm8323", 0 },
 +      { }
 +};
 +
 +static struct i2c_driver lm8323_i2c_driver = {
 +      .driver = {
 +              .name    = "lm8323",
 +      },
 +      .probe          = lm8323_probe,
 +      .remove         = lm8323_remove,
 +      .suspend        = lm8323_suspend,
 +      .resume         = lm8323_resume,
 +      .id_table       = lm8323_id,
 +};
 +MODULE_DEVICE_TABLE(i2c, lm8323_id);
 +
 +static int __init lm8323_init(void)
 +{
 +      return i2c_add_driver(&lm8323_i2c_driver);
 +}
 +module_init(lm8323_init);
 +
 +static void __exit lm8323_exit(void)
 +{
 +      i2c_del_driver(&lm8323_i2c_driver);
 +}
 +module_exit(lm8323_exit);
 +
 +MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
 +MODULE_AUTHOR("Daniel Stone");
 +MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
 +MODULE_DESCRIPTION("LM8323 keypad driver");
 +MODULE_LICENSE("GPL");
 +
index a974a5fdc2e67a17f7efde09bb7e7b0b2c2779c3,0000000000000000000000000000000000000000..0f2cb7f5a10e69c9a4a2caad0cf75c768031f766
mode 100644,000000..100644
--- /dev/null
@@@ -1,475 -1,0 +1,475 @@@
-       snprintf(kp->phys, sizeof(kp->phys), "%s/input-kp", tsc->spi->dev.bus_id);
 +/*
 + * TSC2301 keypad driver
 + *
 + * Copyright (C) 2005-2006 Nokia Corporation
 + *
 + * Written by Jarkko Oikarinen
 + * Rewritten by Juha Yrjola <juha.yrjola@nokia.com>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/input.h>
 +#include <linux/interrupt.h>
 +#include <linux/irq.h>
 +#include <linux/delay.h>
 +#include <linux/spi/spi.h>
 +
 +#include <linux/spi/tsc2301.h>
 +
 +#define TSC2301_KEYBOARD_PRODUCT_ID      0x0051
 +#define TSC2301_KEYBOARD_PRODUCT_VERSION 0x0001
 +#define TSC2301_DEBOUNCE_TIME_2MS        0x0000
 +#define TSC2301_DEBOUNCE_TIME_10MS       0x0800
 +#define TSC2301_DEBOUNCE_TIME_20MS       0x1000
 +#define TSC2301_DEBOUNCE_TIME_50MS       0x1800
 +#define TSC2301_DEBOUNCE_TIME_60MS       0x2000
 +#define TSC2301_DEBOUNCE_TIME_80MS       0x2800
 +#define TSC2301_DEBOUNCE_TIME_100MS      0x3000
 +#define TSC2301_DEBOUNCE_TIME_120MS      0x3800
 +
 +#define TSC2301_DEBOUNCE_TIME         TSC2301_DEBOUNCE_TIME_20MS
 +
 +#define TSC2301_RELEASE_TIMEOUT               50
 +
 +struct tsc2301_kp {
 +      struct input_dev        *idev;
 +      char                    phys[32];
 +      spinlock_t              lock;
 +      struct mutex            mutex;
 +      struct timer_list       timer;
 +      u16                     keys_pressed;
 +      unsigned                pending:1;
 +      unsigned                user_disabled:1;
 +      unsigned                disable_depth;
 +
 +      struct spi_transfer     read_xfer[4];
 +      struct spi_message      read_msg;
 +
 +      u16                     data;
 +      u16                     mask;
 +
 +      int                     irq;
 +      s16                     keymap[16];
 +};
 +
 +static inline int tsc2301_kp_disabled(struct tsc2301 *tsc)
 +{
 +      return tsc->kp->disable_depth != 0;
 +}
 +
 +static void tsc2301_kp_send_key_events(struct tsc2301 *tsc,
 +                                     u16 prev_state,
 +                                     u16 new_state)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +      u16 common, released, pressed;
 +      int i;
 +
 +      common = prev_state & new_state;
 +      released = common ^ prev_state;
 +      pressed = common ^ new_state;
 +      if (!released && !pressed)
 +              return;
 +      for (i = 0; i < 16 && (released || pressed); i++) {
 +              if (released & 1) {
 +                      dev_dbg(&tsc->spi->dev, "key %d released\n", i);
 +                      input_report_key(kp->idev, kp->keymap[i], 0);
 +              }
 +              released >>= 1;
 +              if (pressed & 1) {
 +                      dev_dbg(&tsc->spi->dev, "key %d pressed\n", i);
 +                      input_report_key(kp->idev, kp->keymap[i], 1);
 +              }
 +              pressed >>= 1;
 +      }
 +      input_sync(kp->idev);
 +}
 +
 +static inline void _filter_out(struct tsc2301 *tsc, u16 prev_state,
 +                             u16 *new_state, int row1, int row2, u8 rect_pat)
 +{
 +      u16 mask;
 +
 +      mask = (rect_pat << (row1 * 4)) | (rect_pat << (row2 * 4));
 +      mask &= ~prev_state;
 +      *new_state &= ~mask;
 +      dev_dbg(&tsc->spi->dev, "filtering ghost keys %02x\n", mask);
 +}
 +
 +static void tsc2301_filter_ghost_keys(struct tsc2301 *tsc, u16 prev_state,
 +                                    u16 *new_state)
 +{
 +      int row1, row2;
 +      u16 key_map;
 +      u16 row1_map;
 +      static const u8 rect_pat[] = {
 +              0x3, 0x5, 0x9, 0x6, 0xa, 0xc, 0,
 +      };
 +
 +      key_map = *new_state;
 +      for (row1 = 0; row1 < 4; row1++) {
 +              row1_map = (key_map >> (row1 * 4)) & 0xf;
 +              if (!row1_map)
 +                      continue;
 +              for (row2 = row1 + 1; row2 < 4; row2++) {
 +                      u16 rect_map = (key_map >> (row2 * 4)) & 0xf;
 +                      const u8 *rp;
 +
 +                      rect_map &= row1_map;
 +                      if (!rect_map)
 +                              continue;
 +                      for (rp = rect_pat; *rp; rp++)
 +                              if ((rect_map & *rp) == *rp)
 +                                      _filter_out(tsc, prev_state, new_state,
 +                                                  row1, row2, *rp);
 +              }
 +      }
 +}
 +
 +static void tsc2301_kp_timer(unsigned long arg)
 +{
 +      struct tsc2301 *tsc = (void *) arg;
 +      struct tsc2301_kp *kp = tsc->kp;
 +      unsigned long flags;
 +
 +      tsc2301_kp_send_key_events(tsc, kp->keys_pressed, 0);
 +      spin_lock_irqsave(&kp->lock, flags);
 +      kp->keys_pressed = 0;
 +      spin_unlock_irqrestore(&kp->lock, flags);
 +}
 +
 +static void tsc2301_kp_rx(void *arg)
 +{
 +      struct tsc2301 *tsc = arg;
 +      struct tsc2301_kp *kp = tsc->kp;
 +      unsigned long flags;
 +      u16 kp_data;
 +
 +      kp_data = kp->data;
 +      dev_dbg(&tsc->spi->dev, "KP data %04x\n", kp_data);
 +
 +      tsc2301_filter_ghost_keys(tsc, kp->keys_pressed, &kp_data);
 +      tsc2301_kp_send_key_events(tsc, kp->keys_pressed, kp_data);
 +      spin_lock_irqsave(&kp->lock, flags);
 +      kp->keys_pressed = kp_data;
 +      kp->pending = 0;
 +      spin_unlock_irqrestore(&kp->lock, flags);
 +}
 +
 +static irqreturn_t tsc2301_kp_irq_handler(int irq, void *dev_id)
 +{
 +      struct tsc2301 *tsc = dev_id;
 +      struct tsc2301_kp *kp = tsc->kp;
 +      unsigned long flags;
 +      int r;
 +
 +      spin_lock_irqsave(&kp->lock, flags);
 +      if (tsc2301_kp_disabled(tsc)) {
 +              spin_unlock_irqrestore(&kp->lock, flags);
 +              return IRQ_HANDLED;
 +      }
 +      kp->pending = 1;
 +      spin_unlock_irqrestore(&kp->lock, flags);
 +      mod_timer(&kp->timer,
 +               jiffies + msecs_to_jiffies(TSC2301_RELEASE_TIMEOUT));
 +      r = spi_async(tsc->spi, &tsc->kp->read_msg);
 +      if (r)
 +              dev_err(&tsc->spi->dev, "kp: spi_async() failed");
 +      return IRQ_HANDLED;
 +}
 +
 +static void tsc2301_kp_start_scan(struct tsc2301 *tsc)
 +{
 +      tsc2301_write_reg(tsc, TSC2301_REG_KPMASK, tsc->kp->mask);
 +      tsc2301_write_reg(tsc, TSC2301_REG_KEY, TSC2301_DEBOUNCE_TIME);
 +}
 +
 +static void tsc2301_kp_stop_scan(struct tsc2301 *tsc)
 +{
 +      tsc2301_write_reg(tsc, TSC2301_REG_KEY, 1 << 14);
 +}
 +
 +/* Must be called with the mutex held */
 +static void tsc2301_kp_enable(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&kp->lock, flags);
 +      BUG_ON(!tsc2301_kp_disabled(tsc));
 +      if (--kp->disable_depth != 0) {
 +              spin_unlock_irqrestore(&kp->lock, flags);
 +              return;
 +      }
 +      spin_unlock_irqrestore(&kp->lock, flags);
 +
 +      set_irq_type(kp->irq, IRQ_TYPE_EDGE_FALLING);
 +      tsc2301_kp_start_scan(tsc);
 +      enable_irq(kp->irq);
 +}
 +
 +/* Must be called with the mutex held */
 +static int tsc2301_kp_disable(struct tsc2301 *tsc, int release_keys)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&kp->lock, flags);
 +      if (kp->disable_depth++ != 0) {
 +              spin_unlock_irqrestore(&kp->lock, flags);
 +              goto out;
 +      }
 +      disable_irq_nosync(kp->irq);
 +      set_irq_type(kp->irq, IRQ_TYPE_NONE);
 +      spin_unlock_irqrestore(&kp->lock, flags);
 +
 +      while (kp->pending) {
 +              msleep(1);
 +      }
 +
 +      tsc2301_kp_stop_scan(tsc);
 +out:
 +      if (!release_keys)
 +              del_timer(&kp->timer); /* let timeout release keys */
 +
 +      return 0;
 +}
 +
 +/* The following workaround is needed for a HW bug triggered by the
 + * following:
 + * 1. keep any key pressed
 + * 2. disable keypad
 + * 3. release all keys
 + * 4. reenable keypad
 + * 5. disable touch screen controller
 + *
 + * After this the keypad scanner will get stuck in busy state and won't
 + * report any interrupts for further keypresses. One way to recover is to
 + * restart the keypad scanner whenever we enable / disable the
 + * touchscreen controller.
 + */
 +void tsc2301_kp_restart(struct tsc2301 *tsc)
 +{
 +      if (!tsc2301_kp_disabled(tsc)) {
 +              tsc2301_kp_start_scan(tsc);
 +      }
 +}
 +
 +static ssize_t tsc2301_kp_disable_show(struct device *dev,
 +                                     struct device_attribute *attr, char *buf)
 +{
 +      struct tsc2301          *tsc = dev_get_drvdata(dev);
 +
 +      return sprintf(buf, "%u\n", tsc2301_kp_disabled(tsc) ? 1 : 0);
 +}
 +
 +static ssize_t tsc2301_kp_disable_store(struct device *dev,
 +                                      struct device_attribute *attr,
 +                                      const char *buf, size_t count)
 +{
 +      struct tsc2301          *tsc = dev_get_drvdata(dev);
 +      struct tsc2301_kp       *kp = tsc->kp;
 +      char *endp;
 +      int i;
 +
 +      i = simple_strtoul(buf, &endp, 10);
 +      i = i ? 1 : 0;
 +
 +      mutex_lock(&kp->mutex);
 +      if (i == kp->user_disabled) {
 +              mutex_unlock(&kp->mutex);
 +              return count;
 +      }
 +      kp->user_disabled = i;
 +
 +      if (i)
 +              tsc2301_kp_disable(tsc, 1);
 +      else
 +              tsc2301_kp_enable(tsc);
 +      mutex_unlock(&kp->mutex);
 +
 +      return count;
 +}
 +
 +static DEVICE_ATTR(disable_kp, 0664, tsc2301_kp_disable_show,
 +                 tsc2301_kp_disable_store);
 +
 +static const u16 tsc2301_kp_read_data = 0x8000 | TSC2301_REG_KPDATA;
 +
 +static void tsc2301_kp_setup_spi_xfer(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +      struct spi_message *m = &kp->read_msg;
 +      struct spi_transfer *x = &kp->read_xfer[0];
 +
 +      spi_message_init(&kp->read_msg);
 +
 +      x->tx_buf = &tsc2301_kp_read_data;
 +      x->len = 2;
 +      spi_message_add_tail(x, m);
 +      x++;
 +
 +      x->rx_buf = &kp->data;
 +      x->len = 2;
 +      spi_message_add_tail(x, m);
 +
 +      m->complete = tsc2301_kp_rx;
 +      m->context = tsc;
 +}
 +
 +#ifdef CONFIG_PM
 +int tsc2301_kp_suspend(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +
 +      mutex_lock(&kp->mutex);
 +      tsc2301_kp_disable(tsc, 1);
 +      mutex_unlock(&kp->mutex);
 +      return 0;
 +}
 +
 +void tsc2301_kp_resume(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +
 +      mutex_lock(&kp->mutex);
 +      tsc2301_kp_enable(tsc);
 +      mutex_unlock(&kp->mutex);
 +}
 +#endif
 +
 +int __devinit tsc2301_kp_init(struct tsc2301 *tsc,
 +                            struct tsc2301_platform_data *pdata)
 +{
 +      struct input_dev *idev;
 +      struct tsc2301_kp *kp;
 +      int r, i;
 +      u16 mask;
 +
 +      if (pdata->keyb_int < 0) {
 +              dev_err(&tsc->spi->dev, "need kbirq");
 +              return -EINVAL;
 +      }
 +
 +      kp = kzalloc(sizeof(*kp), GFP_KERNEL);
 +      if (kp == NULL)
 +              return -ENOMEM;
 +      tsc->kp = kp;
 +
 +      kp->irq = pdata->keyb_int;
 +      spin_lock_init(&kp->lock);
 +      mutex_init(&kp->mutex);
 +
 +      init_timer(&kp->timer);
 +      kp->timer.data = (unsigned long) tsc;
 +      kp->timer.function = tsc2301_kp_timer;
 +
 +      idev = input_allocate_device();
 +      if (idev == NULL) {
 +              r = -ENOMEM;
 +              goto err1;
 +      }
 +      if (pdata->keyb_name)
 +              idev->name = pdata->keyb_name;
 +      else
 +              idev->name = "TSC2301 keypad";
++      snprintf(kp->phys, sizeof(kp->phys), "%s/input-kp", dev_name(&tsc->spi->dev));
 +      idev->phys = kp->phys;
 +
 +      mask = 0;
 +      idev->evbit[0] = BIT(EV_KEY);
 +      for (i = 0; i < 16; i++) {
 +              if (pdata->keymap[i] > 0) {
 +                      set_bit(pdata->keymap[i], idev->keybit);
 +                      kp->keymap[i] = pdata->keymap[i];
 +              } else {
 +                      kp->keymap[i] = -1;
 +                      mask |= 1 << i;
 +              }
 +      }
 +
 +      if (pdata->kp_rep)
 +              set_bit(EV_REP, idev->evbit);
 +
 +      kp->idev = idev;
 +
 +      tsc2301_kp_setup_spi_xfer(tsc);
 +
 +      r = device_create_file(&tsc->spi->dev, &dev_attr_disable_kp);
 +      if (r < 0)
 +              goto err2;
 +
 +      tsc2301_kp_start_scan(tsc);
 +
 +      /* IRQ mode 0 is faulty, it can cause the KBIRQ to get stuck.
 +       * Mode 2 deasserts the IRQ at:
 +       * - HW or SW reset
 +       * - Setting SCS flag in REG_KEY register
 +       * - Releasing all keys
 +       * - Reading the REG_KPDATA
 +       */
 +      tsc2301_write_kbc(tsc, 2);
 +
 +      tsc2301_write_reg(tsc, TSC2301_REG_KPMASK, mask);
 +      kp->mask = mask;
 +
 +      set_irq_type(kp->irq, IRQ_TYPE_EDGE_FALLING);
 +
 +      r = request_irq(kp->irq, tsc2301_kp_irq_handler, IRQF_SAMPLE_RANDOM,
 +                      "tsc2301-kp", tsc);
 +      if (r < 0) {
 +              dev_err(&tsc->spi->dev, "unable to get kbirq IRQ");
 +              goto err3;
 +      }
 +      set_irq_wake(kp->irq, 1);
 +
 +      /* We need to read the register once..? */
 +      tsc2301_read_reg(tsc, TSC2301_REG_KPDATA);
 +
 +      r = input_register_device(idev);
 +      if (r < 0) {
 +              dev_err(&tsc->spi->dev, "can't register keypad device\n");
 +              goto err4;
 +      }
 +
 +      return 0;
 +
 +err4:
 +      free_irq(kp->irq, tsc);
 +err3:
 +      tsc2301_kp_stop_scan(tsc);
 +      device_remove_file(&tsc->spi->dev, &dev_attr_disable_kp);
 +err2:
 +      input_free_device(kp->idev);
 +err1:
 +      kfree(kp);
 +      return r;
 +}
 +
 +void __devexit tsc2301_kp_exit(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_kp *kp = tsc->kp;
 +
 +      tsc2301_kp_disable(tsc, 1);
 +      input_unregister_device(kp->idev);
 +      free_irq(kp->irq, tsc);
 +      device_remove_file(&tsc->spi->dev, &dev_attr_disable_kp);
 +
 +      kfree(kp);
 +}
index 03c3a102dd61f7e6c844b1d5cd54bc20c3896975,0000000000000000000000000000000000000000..ec4c9e7a736753a7070af69cbddcbd551d0a0f1d
mode 100644,000000..100644
--- /dev/null
@@@ -1,728 -1,0 +1,728 @@@
-                ts->spi->dev.bus_id);
 +/*
 + * TSC2005 touchscreen driver
 + *
 + * Copyright (C) 2006-2008 Nokia Corporation
 + *
 + * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
 + * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/input.h>
 +#include <linux/interrupt.h>
 +#include <linux/delay.h>
 +#include <linux/gpio.h>
 +#include <linux/spi/spi.h>
 +
 +#include <linux/spi/tsc2005.h>
 +
 +/**
 + * The touchscreen interface operates as follows:
 + *
 + * Initialize:
 + *    Request access to GPIO103 (DAV)
 + *    tsc2005_dav_irq_handler will trigger when DAV line goes down
 + *
 + *  1) Pen is pressed against touchscreeen
 + *  2) TSC2005 performs AD conversion
 + *  3) After the conversion is done TSC2005 drives DAV line down
 + *  4) GPIO IRQ is received and tsc2005_dav_irq_handler is called
 + *  5) tsc2005_ts_irq_handler queues up an spi transfer to fetch
 + *     the x, y, z1, z2 values
 + *  6) tsc2005_ts_rx() reports coordinates to input layer and
 + *     sets up tsc2005_ts_timer() to be called after TSC2005_TS_SCAN_TIME
 + *  7)  When the penup_timer expires, there have not been DAV interrupts
 + *     during the last 20ms which means the pen has been lifted.
 + */
 +
 +#define TSC2005_VDD_LOWER_27
 +
 +#ifdef TSC2005_VDD_LOWER_27
 +#define TSC2005_HZ     (10000000)
 +#else
 +#define TSC2005_HZ     (25000000)
 +#endif
 +
 +#define TSC2005_CMD   (0x80)
 +#define TSC2005_REG   (0x00)
 +
 +#define TSC2005_CMD_STOP      (1)
 +#define TSC2005_CMD_10BIT     (0 << 2)
 +#define TSC2005_CMD_12BIT     (1 << 2)
 +
 +#define TSC2005_CMD_SCAN_XYZZ (0 << 3)
 +#define TSC2005_CMD_SCAN_XY   (1 << 3)
 +#define TSC2005_CMD_SCAN_X    (2 << 3)
 +#define TSC2005_CMD_SCAN_Y    (3 << 3)
 +#define TSC2005_CMD_SCAN_ZZ   (4 << 3)
 +#define TSC2005_CMD_AUX_SINGLE        (5 << 3)
 +#define TSC2005_CMD_TEMP1     (6 << 3)
 +#define TSC2005_CMD_TEMP2     (7 << 3)
 +#define TSC2005_CMD_AUX_CONT  (8 << 3)
 +#define TSC2005_CMD_TEST_X_CONN       (9 << 3)
 +#define TSC2005_CMD_TEST_Y_CONN       (10 << 3)
 +/* command 11 reserved */
 +#define TSC2005_CMD_TEST_SHORT        (12 << 3)
 +#define TSC2005_CMD_DRIVE_XX  (13 << 3)
 +#define TSC2005_CMD_DRIVE_YY  (14 << 3)
 +#define TSC2005_CMD_DRIVE_YX  (15 << 3)
 +
 +#define TSC2005_REG_X         (0 << 3)
 +#define TSC2005_REG_Y         (1 << 3)
 +#define TSC2005_REG_Z1                (2 << 3)
 +#define TSC2005_REG_Z2                (3 << 3)
 +#define TSC2005_REG_AUX               (4 << 3)
 +#define TSC2005_REG_TEMP1     (5 << 3)
 +#define TSC2005_REG_TEMP2     (6 << 3)
 +#define TSC2005_REG_STATUS    (7 << 3)
 +#define TSC2005_REG_AUX_HIGH  (8 << 3)
 +#define TSC2005_REG_AUX_LOW   (9 << 3)
 +#define TSC2005_REG_TEMP_HIGH (10 << 3)
 +#define TSC2005_REG_TEMP_LOW  (11 << 3)
 +#define TSC2005_REG_CFR0      (12 << 3)
 +#define TSC2005_REG_CFR1      (13 << 3)
 +#define TSC2005_REG_CFR2      (14 << 3)
 +#define TSC2005_REG_FUNCTION  (15 << 3)
 +
 +#define TSC2005_REG_PND0      (1 << 1)
 +#define TSC2005_REG_READ      (0x01)
 +#define TSC2005_REG_WRITE     (0x00)
 +
 +
 +#define TSC2005_CFR0_LONGSAMPLING     (1)
 +#define TSC2005_CFR0_DETECTINWAIT     (1 << 1)
 +#define TSC2005_CFR0_SENSETIME_32US   (0)
 +#define TSC2005_CFR0_SENSETIME_96US   (1 << 2)
 +#define TSC2005_CFR0_SENSETIME_544US  (1 << 3)
 +#define TSC2005_CFR0_SENSETIME_2080US (1 << 4)
 +#define TSC2005_CFR0_SENSETIME_2656US (0x001C)
 +#define TSC2005_CFR0_PRECHARGE_20US   (0x0000)
 +#define TSC2005_CFR0_PRECHARGE_84US   (0x0020)
 +#define TSC2005_CFR0_PRECHARGE_276US  (0x0040)
 +#define TSC2005_CFR0_PRECHARGE_1044US (0x0080)
 +#define TSC2005_CFR0_PRECHARGE_1364US (0x00E0)
 +#define TSC2005_CFR0_STABTIME_0US     (0x0000)
 +#define TSC2005_CFR0_STABTIME_100US   (0x0100)
 +#define TSC2005_CFR0_STABTIME_500US   (0x0200)
 +#define TSC2005_CFR0_STABTIME_1MS     (0x0300)
 +#define TSC2005_CFR0_STABTIME_5MS     (0x0400)
 +#define TSC2005_CFR0_STABTIME_100MS   (0x0700)
 +#define TSC2005_CFR0_CLOCK_4MHZ               (0x0000)
 +#define TSC2005_CFR0_CLOCK_2MHZ               (0x0800)
 +#define TSC2005_CFR0_CLOCK_1MHZ               (0x1000)
 +#define TSC2005_CFR0_RESOLUTION12     (0x2000)
 +#define TSC2005_CFR0_STATUS           (0x4000)
 +#define TSC2005_CFR0_PENMODE          (0x8000)
 +
 +#define TSC2005_CFR0_INITVALUE        (TSC2005_CFR0_STABTIME_1MS  |   \
 +                               TSC2005_CFR0_CLOCK_1MHZ    |   \
 +                               TSC2005_CFR0_RESOLUTION12  |   \
 +                               TSC2005_CFR0_PRECHARGE_276US | \
 +                               TSC2005_CFR0_PENMODE)
 +
 +#define TSC2005_CFR1_BATCHDELAY_0MS   (0x0000)
 +#define TSC2005_CFR1_BATCHDELAY_1MS   (0x0001)
 +#define TSC2005_CFR1_BATCHDELAY_2MS   (0x0002)
 +#define TSC2005_CFR1_BATCHDELAY_4MS   (0x0003)
 +#define TSC2005_CFR1_BATCHDELAY_10MS  (0x0004)
 +#define TSC2005_CFR1_BATCHDELAY_20MS  (0x0005)
 +#define TSC2005_CFR1_BATCHDELAY_40MS  (0x0006)
 +#define TSC2005_CFR1_BATCHDELAY_100MS (0x0007)
 +
 +#define TSC2005_CFR1_INITVALUE        (TSC2005_CFR1_BATCHDELAY_2MS)
 +
 +#define TSC2005_CFR2_MAVE_TEMP        (0x0001)
 +#define TSC2005_CFR2_MAVE_AUX (0x0002)
 +#define TSC2005_CFR2_MAVE_Z   (0x0004)
 +#define TSC2005_CFR2_MAVE_Y   (0x0008)
 +#define TSC2005_CFR2_MAVE_X   (0x0010)
 +#define TSC2005_CFR2_AVG_1    (0x0000)
 +#define TSC2005_CFR2_AVG_3    (0x0400)
 +#define TSC2005_CFR2_AVG_7    (0x0800)
 +#define TSC2005_CFR2_MEDIUM_1 (0x0000)
 +#define TSC2005_CFR2_MEDIUM_3 (0x1000)
 +#define TSC2005_CFR2_MEDIUM_7 (0x2000)
 +#define TSC2005_CFR2_MEDIUM_15        (0x3000)
 +
 +#define TSC2005_CFR2_IRQ_DAV  (0x4000)
 +#define TSC2005_CFR2_IRQ_PEN  (0x8000)
 +#define TSC2005_CFR2_IRQ_PENDAV       (0x0000)
 +
 +#define TSC2005_CFR2_INITVALUE        (TSC2005_CFR2_IRQ_DAV   |       \
 +                               TSC2005_CFR2_MAVE_X    |       \
 +                               TSC2005_CFR2_MAVE_Y    |       \
 +                               TSC2005_CFR2_MAVE_Z    |       \
 +                               TSC2005_CFR2_MEDIUM_15 |       \
 +                               TSC2005_CFR2_AVG_7)
 +
 +#define MAX_12BIT                                     ((1 << 12) - 1)
 +#define TS_SAMPLES                                    4
 +#define TS_RECT_SIZE                                  8
 +#define TSC2005_TS_PENUP_TIME                         20
 +
 +static const u32 tsc2005_read_reg[] = {
 +      (TSC2005_REG | TSC2005_REG_X | TSC2005_REG_READ) << 16,
 +      (TSC2005_REG | TSC2005_REG_Y | TSC2005_REG_READ) << 16,
 +      (TSC2005_REG | TSC2005_REG_Z1 | TSC2005_REG_READ) << 16,
 +      (TSC2005_REG | TSC2005_REG_Z2 | TSC2005_REG_READ) << 16,
 +};
 +#define NUM_READ_REGS (sizeof(tsc2005_read_reg)/sizeof(tsc2005_read_reg[0]))
 +
 +struct tsc2005 {
 +      struct spi_device       *spi;
 +
 +      struct input_dev        *idev;
 +      char                    phys[32];
 +      struct timer_list       penup_timer;
 +      spinlock_t              lock;
 +      struct mutex            mutex;
 +
 +      struct spi_message      read_msg;
 +      struct spi_transfer     read_xfer[NUM_READ_REGS];
 +      u32                     data[NUM_READ_REGS];
 +
 +      /* previous x,y,z */
 +      int                     x;
 +      int                     y;
 +      int                     p;
 +      /* average accumulators for each component */
 +      int                     sample_cnt;
 +      int                     avg_x;
 +      int                     avg_y;
 +      int                     avg_z1;
 +      int                     avg_z2;
 +      /* configuration */
 +      int                     x_plate_ohm;
 +      int                     hw_avg_max;
 +      int                     stab_time;
 +      int                     p_max;
 +      int                     touch_pressure;
 +      int                     irq;
 +      s16                     dav_gpio;
 +      /* status */
 +      u8                      sample_sent;
 +      u8                      pen_down;
 +      u8                      disabled;
 +      u8                      disable_depth;
 +      u8                      spi_active;
 +};
 +
 +static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
 +{
 +      u16 data = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
 +      struct spi_message msg;
 +      struct spi_transfer xfer = { 0 };
 +
 +      xfer.tx_buf = &data;
 +      xfer.rx_buf = NULL;
 +      xfer.len = 1;
 +      xfer.bits_per_word = 8;
 +
 +      spi_message_init(&msg);
 +      spi_message_add_tail(&xfer, &msg);
 +      spi_sync(ts->spi, &msg);
 +}
 +
 +static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
 +{
 +      u32 tx;
 +      struct spi_message msg;
 +      struct spi_transfer xfer = { 0 };
 +
 +      tx = (TSC2005_REG | reg | TSC2005_REG_PND0 |
 +             TSC2005_REG_WRITE) << 16;
 +      tx |= value;
 +
 +      xfer.tx_buf = &tx;
 +      xfer.rx_buf = NULL;
 +      xfer.len = 4;
 +      xfer.bits_per_word = 24;
 +
 +      spi_message_init(&msg);
 +      spi_message_add_tail(&xfer, &msg);
 +      spi_sync(ts->spi, &msg);
 +}
 +
 +static void tsc2005_ts_update_pen_state(struct tsc2005 *ts,
 +                                      int x, int y, int pressure)
 +{
 +      if (pressure) {
 +              input_report_abs(ts->idev, ABS_X, x);
 +              input_report_abs(ts->idev, ABS_Y, y);
 +              input_report_abs(ts->idev, ABS_PRESSURE, pressure);
 +              if (!ts->pen_down) {
 +                      input_report_key(ts->idev, BTN_TOUCH, 1);
 +                      ts->pen_down = 1;
 +              }
 +      } else {
 +              input_report_abs(ts->idev, ABS_PRESSURE, 0);
 +              if (ts->pen_down) {
 +                      input_report_key(ts->idev, BTN_TOUCH, 0);
 +                      ts->pen_down = 0;
 +              }
 +      }
 +
 +      input_sync(ts->idev);
 +}
 +
 +/*
 + * This function is called by the SPI framework after the coordinates
 + * have been read from TSC2005
 + */
 +static void tsc2005_ts_rx(void *arg)
 +{
 +      struct tsc2005 *ts = arg;
 +      unsigned long flags;
 +      int inside_rect, pressure_limit;
 +      int x, y, z1, z2, pressure;
 +
 +      spin_lock_irqsave(&ts->lock, flags);
 +
 +      x = ts->data[0];
 +      y = ts->data[1];
 +      z1 = ts->data[2];
 +      z2 = ts->data[3];
 +
 +      /* validate pressure and position */
 +      if (x > MAX_12BIT || y > MAX_12BIT)
 +              goto out;
 +
 +      /* skip coords if the pressure-components are out of range */
 +      if (z1 < 100 || z2 > 4000)
 +              goto out;
 +
 +      /* don't run average on the "pen down" event */
 +      if (ts->sample_sent) {
 +              ts->avg_x += x;
 +              ts->avg_y += y;
 +              ts->avg_z1 += z1;
 +              ts->avg_z2 += z2;
 +
 +              if (++ts->sample_cnt < TS_SAMPLES)
 +                      goto out;
 +
 +              x = ts->avg_x / TS_SAMPLES;
 +              y = ts->avg_y / TS_SAMPLES;
 +              z1 = ts->avg_z1 / TS_SAMPLES;
 +              z2 = ts->avg_z2 / TS_SAMPLES;
 +      }
 +
 +      ts->sample_cnt = 0;
 +      ts->avg_x = 0;
 +      ts->avg_y = 0;
 +      ts->avg_z1 = 0;
 +      ts->avg_z2 = 0;
 +
 +      if (z1) {
 +              pressure = x * (z2 - z1) / z1;
 +              pressure = pressure * ts->x_plate_ohm / 4096;
 +      } else
 +              goto out;
 +
 +      pressure_limit = ts->sample_sent? ts->p_max: ts->touch_pressure;
 +      if (pressure > pressure_limit)
 +              goto out;
 +
 +      /* discard the event if it still is within the previous rect - unless
 +       * if the pressure is harder, but then use previous x,y position */
 +      inside_rect = (ts->sample_sent &&
 +              x > (int)ts->x - TS_RECT_SIZE &&
 +              x < (int)ts->x + TS_RECT_SIZE &&
 +              y > (int)ts->y - TS_RECT_SIZE &&
 +              y < (int)ts->y + TS_RECT_SIZE);
 +      if (inside_rect)
 +              x = ts->x, y = ts->y;
 +
 +      if (!inside_rect || pressure < ts->p) {
 +              tsc2005_ts_update_pen_state(ts, x, y, pressure);
 +              ts->sample_sent = 1;
 +              ts->x = x;
 +              ts->y = y;
 +              ts->p = pressure;
 +      }
 +out:
 +      ts->spi_active = 0;
 +      spin_unlock_irqrestore(&ts->lock, flags);
 +
 +      /* kick pen up timer - to make sure it expires again(!) */
 +      if (ts->sample_sent)
 +              mod_timer(&ts->penup_timer,
 +                        jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
 +}
 +
 +static void tsc2005_ts_penup_timer_handler(unsigned long data)
 +{
 +      struct tsc2005 *ts = (struct tsc2005 *)data;
 +
 +      if (ts->sample_sent) {
 +              tsc2005_ts_update_pen_state(ts, 0, 0, 0);
 +              ts->sample_sent = 0;
 +      }
 +}
 +
 +/*
 + * This interrupt is called when pen is down and coordinates are
 + * available. That is indicated by a falling edge on DAV line.
 + */
 +static irqreturn_t tsc2005_ts_irq_handler(int irq, void *dev_id)
 +{
 +      struct tsc2005 *ts = dev_id;
 +      int r;
 +
 +      if (ts->spi_active)
 +              return IRQ_HANDLED;
 +
 +      ts->spi_active = 1;
 +      r = spi_async(ts->spi, &ts->read_msg);
 +      if (r)
 +              dev_err(&ts->spi->dev, "ts: spi_async() failed");
 +
 +      /* kick pen up timer */
 +      mod_timer(&ts->penup_timer,
 +                jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
 +
 +      return IRQ_HANDLED;
 +}
 +
 +static void tsc2005_ts_setup_spi_xfer(struct tsc2005 *ts)
 +{
 +      struct spi_message *m = &ts->read_msg;
 +      struct spi_transfer *x = &ts->read_xfer[0];
 +      int i;
 +
 +      spi_message_init(m);
 +
 +      for (i = 0; i < NUM_READ_REGS; i++, x++) {
 +              x->tx_buf = &tsc2005_read_reg[i];
 +              x->rx_buf = &ts->data[i];
 +              x->len = 4;
 +              x->bits_per_word = 24;
 +              x->cs_change = i < (NUM_READ_REGS - 1);
 +              spi_message_add_tail(x, m);
 +      }
 +
 +      m->complete = tsc2005_ts_rx;
 +      m->context = ts;
 +}
 +
 +static ssize_t tsc2005_ts_pen_down_show(struct device *dev,
 +                                      struct device_attribute *attr,
 +                                      char *buf)
 +{
 +      struct tsc2005 *tsc = dev_get_drvdata(dev);
 +
 +      return sprintf(buf, "%u\n", tsc->pen_down);
 +}
 +
 +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2005_ts_pen_down_show, NULL);
 +
 +static int tsc2005_configure(struct tsc2005 *tsc, int flags)
 +{
 +      tsc2005_write(tsc, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
 +      tsc2005_write(tsc, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
 +      tsc2005_write(tsc, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
 +      tsc2005_cmd(tsc, flags);
 +
 +      return 0;
 +}
 +
 +static void tsc2005_start_scan(struct tsc2005 *tsc)
 +{
 +      tsc2005_configure(tsc, TSC2005_CMD_SCAN_XYZZ);
 +}
 +
 +static void tsc2005_stop_scan(struct tsc2005 *tsc)
 +{
 +      tsc2005_cmd(tsc, TSC2005_CMD_STOP);
 +}
 +
 +/* Must be called with mutex held */
 +static void tsc2005_disable(struct tsc2005 *ts)
 +{
 +      if (ts->disable_depth++ != 0)
 +              return;
 +
 +      disable_irq(ts->irq);
 +
 +      /* wait until penup timer expire normally */
 +      do {
 +              msleep(4);
 +      } while (ts->sample_sent);
 +
 +      tsc2005_stop_scan(ts);
 +}
 +
 +static void tsc2005_enable(struct tsc2005 *ts)
 +{
 +      if (--ts->disable_depth != 0)
 +              return;
 +
 +      enable_irq(ts->irq);
 +
 +      tsc2005_start_scan(ts);
 +}
 +
 +static ssize_t tsc2005_disable_show(struct device *dev,
 +                                  struct device_attribute *attr, char *buf)
 +{
 +      struct tsc2005 *ts = dev_get_drvdata(dev);
 +
 +      return sprintf(buf, "%u\n", ts->disabled);
 +}
 +
 +static ssize_t tsc2005_disable_store(struct device *dev,
 +                                   struct device_attribute *attr,
 +                                   const char *buf, size_t count)
 +{
 +      struct tsc2005          *tsc = dev_get_drvdata(dev);
 +      unsigned long res;
 +      int i;
 +
 +      i = strict_strtoul(buf, 10, &res);
 +      i = i ? 1 : 0;
 +
 +      mutex_lock(&tsc->mutex);
 +      if (i == tsc->disabled)
 +              goto out;
 +      tsc->disabled = i;
 +
 +      if (i)
 +              tsc2005_disable(tsc);
 +      else
 +              tsc2005_enable(tsc);
 +out:
 +      mutex_unlock(&tsc->mutex);
 +      return count;
 +}
 +
 +static DEVICE_ATTR(disable_ts, 0664, tsc2005_disable_show,
 +                 tsc2005_disable_store);
 +
 +
 +static int __devinit tsc2005_ts_init(struct tsc2005 *ts,
 +                                   struct tsc2005_platform_data *pdata)
 +{
 +      struct input_dev *idev;
 +      int dav_gpio, r;
 +      int x_max, y_max;
 +      int x_fudge, y_fudge, p_fudge;
 +
 +      if (pdata->dav_gpio < 0) {
 +              dev_err(&ts->spi->dev, "need DAV GPIO");
 +              return -EINVAL;
 +      }
 +      dav_gpio = pdata->dav_gpio;
 +      ts->dav_gpio = dav_gpio;
 +      dev_dbg(&ts->spi->dev, "TSC2005: DAV GPIO = %d\n", dav_gpio);
 +
 +      r = gpio_request(dav_gpio, "TSC2005 dav");
 +      if (r < 0) {
 +              dev_err(&ts->spi->dev, "unable to get DAV GPIO");
 +              goto err1;
 +      }
 +      gpio_direction_input(dav_gpio);
 +      ts->irq = gpio_to_irq(dav_gpio);
 +      dev_dbg(&ts->spi->dev, "TSC2005: DAV IRQ = %d\n", ts->irq);
 +
 +      init_timer(&ts->penup_timer);
 +      setup_timer(&ts->penup_timer, tsc2005_ts_penup_timer_handler,
 +                      (unsigned long)ts);
 +
 +      spin_lock_init(&ts->lock);
 +      mutex_init(&ts->mutex);
 +
 +      ts->x_plate_ohm         = pdata->ts_x_plate_ohm ? : 280;
 +      ts->hw_avg_max          = pdata->ts_hw_avg;
 +      ts->stab_time           = pdata->ts_stab_time;
 +      x_max                   = pdata->ts_x_max ? : 4096;
 +      x_fudge                 = pdata->ts_x_fudge ? : 4;
 +      y_max                   = pdata->ts_y_max ? : 4096;
 +      y_fudge                 = pdata->ts_y_fudge ? : 8;
 +      ts->p_max               = pdata->ts_pressure_max ? : MAX_12BIT;
 +      ts->touch_pressure      = pdata->ts_touch_pressure ? : ts->p_max;
 +      p_fudge                 = pdata->ts_pressure_fudge ? : 2;
 +
 +      idev = input_allocate_device();
 +      if (idev == NULL) {
 +              r = -ENOMEM;
 +              goto err2;
 +      }
 +
 +      idev->name = "TSC2005 touchscreen";
 +      snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts",
++               dev_name(&ts->spi->dev));
 +      idev->phys = ts->phys;
 +
 +      idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
 +      idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
 +      ts->idev = idev;
 +
 +      tsc2005_ts_setup_spi_xfer(ts);
 +
 +      input_set_abs_params(idev, ABS_X, 0, x_max, x_fudge, 0);
 +      input_set_abs_params(idev, ABS_Y, 0, y_max, y_fudge, 0);
 +      input_set_abs_params(idev, ABS_PRESSURE, 0, ts->p_max, p_fudge, 0);
 +
 +      tsc2005_start_scan(ts);
 +
 +      r = request_irq(ts->irq, tsc2005_ts_irq_handler,
 +                      IRQF_TRIGGER_FALLING | IRQF_DISABLED |
 +                      IRQF_SAMPLE_RANDOM, "tsc2005", ts);
 +      if (r < 0) {
 +              dev_err(&ts->spi->dev, "unable to get DAV IRQ");
 +              goto err3;
 +      }
 +
 +      set_irq_wake(ts->irq, 1);
 +
 +      r = input_register_device(idev);
 +      if (r < 0) {
 +              dev_err(&ts->spi->dev, "can't register touchscreen device\n");
 +              goto err4;
 +      }
 +
 +      /* We can tolerate these failing */
 +      if (device_create_file(&ts->spi->dev, &dev_attr_pen_down));
 +      if (device_create_file(&ts->spi->dev, &dev_attr_disable_ts));
 +
 +      return 0;
 +err4:
 +      free_irq(ts->irq, ts);
 +err3:
 +      tsc2005_stop_scan(ts);
 +      input_free_device(idev);
 +err2:
 +      gpio_free(dav_gpio);
 +err1:
 +      return r;
 +}
 +
 +static int __devinit tsc2005_probe(struct spi_device *spi)
 +{
 +      struct tsc2005                  *tsc;
 +      struct tsc2005_platform_data    *pdata = spi->dev.platform_data;
 +      int r;
 +
 +      if (!pdata) {
 +              dev_dbg(&spi->dev, "no platform data?\n");
 +              return -ENODEV;
 +      }
 +
 +      tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
 +      if (tsc == NULL)
 +              return -ENOMEM;
 +
 +      dev_set_drvdata(&spi->dev, tsc);
 +      tsc->spi = spi;
 +      spi->dev.power.power_state = PMSG_ON;
 +
 +      spi->mode = SPI_MODE_0;
 +      spi->bits_per_word = 8;
 +      /* The max speed might've been defined by the board-specific
 +       * struct */
 +      if (!spi->max_speed_hz)
 +              spi->max_speed_hz = TSC2005_HZ;
 +
 +      spi_setup(spi);
 +
 +      r = tsc2005_ts_init(tsc, pdata);
 +      if (r)
 +              goto err1;
 +
 +      return 0;
 +
 +err1:
 +      kfree(tsc);
 +      return r;
 +}
 +
 +static int __devexit tsc2005_remove(struct spi_device *spi)
 +{
 +      struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
 +
 +      mutex_lock(&ts->mutex);
 +      tsc2005_disable(ts);
 +      mutex_unlock(&ts->mutex);
 +
 +      device_remove_file(&ts->spi->dev, &dev_attr_disable_ts);
 +      device_remove_file(&ts->spi->dev, &dev_attr_pen_down);
 +
 +      free_irq(ts->irq, ts);
 +      input_unregister_device(ts->idev);
 +
 +      gpio_free(ts->dav_gpio);
 +      kfree(ts);
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM
 +static int tsc2005_suspend(struct spi_device *spi, pm_message_t mesg)
 +{
 +      struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
 +
 +      mutex_lock(&ts->mutex);
 +      tsc2005_disable(ts);
 +      mutex_unlock(&ts->mutex);
 +
 +      return 0;
 +}
 +
 +static int tsc2005_resume(struct spi_device *spi)
 +{
 +      struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
 +
 +      mutex_lock(&ts->mutex);
 +      tsc2005_enable(ts);
 +      mutex_unlock(&ts->mutex);
 +
 +      return 0;
 +}
 +#endif
 +
 +static struct spi_driver tsc2005_driver = {
 +      .driver = {
 +              .name = "tsc2005",
 +              .owner = THIS_MODULE,
 +      },
 +#ifdef CONFIG_PM
 +      .suspend = tsc2005_suspend,
 +      .resume = tsc2005_resume,
 +#endif
 +      .probe = tsc2005_probe,
 +      .remove = __devexit_p(tsc2005_remove),
 +};
 +
 +static int __init tsc2005_init(void)
 +{
 +      printk(KERN_INFO "TSC2005 driver initializing\n");
 +
 +      return spi_register_driver(&tsc2005_driver);
 +}
 +module_init(tsc2005_init);
 +
 +static void __exit tsc2005_exit(void)
 +{
 +      spi_unregister_driver(&tsc2005_driver);
 +}
 +module_exit(tsc2005_exit);
 +
 +MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
 +MODULE_LICENSE("GPL");
 +MODULE_ALIAS("platform:tsc2005");
index 6462cc2bf398447e67f60d821401fc85dd025ef8,0000000000000000000000000000000000000000..a157b48dd0b8638a9187ed950be8403a12242d84
mode 100644,000000..100644
--- /dev/null
@@@ -1,676 -1,0 +1,676 @@@
-                "%s/input-ts", tsc->spi->dev.bus_id);
 +/*
 + * TSC2301 touchscreen driver
 + *
 + * Copyright (C) 2005-2008 Nokia Corporation
 + *
 + * Written by Jarkko Oikarinen, Imre Deak and Juha Yrjola
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/input.h>
 +#include <linux/interrupt.h>
 +#include <linux/delay.h>
 +#include <linux/spi/spi.h>
 +
 +#include <linux/spi/tsc2301.h>
 +
 +/**
 + * The touchscreen interface operates as follows:
 + *
 + * Initialize:
 + *    Request access to GPIO103 (DAV)
 + *    tsc2301_ts_irq_handler will trigger when DAV line goes down
 + *
 + *  1) Pen is pressed against touchscreeen
 + *  2) TSC2301 performs AD conversion
 + *  3) After the conversion is done TSC2301 drives DAV line down
 + *  4) GPIO IRQ is received and tsc2301_ts_irq_handler is called
 + *  5) tsc2301_ts_irq_handler queues up an spi transfer to fetch
 + *     the x, y, z1, z2 values
 + *  6) SPI framework calls tsc2301_ts_rx after the coordinates are read
 + *  7) When the penup_timer expires, there have not been DAV interrupts
 + *     during the last 20ms which means the pen has been lifted.
 + */
 +
 +
 +#define TSC2301_TOUCHSCREEN_PRODUCT_ID                0x0052
 +#define TSC2301_TOUCHSCREEN_PRODUCT_VERSION           0x0001
 +
 +#define TSC2301_TS_PENUP_TIME                         20
 +
 +#define TSC2301_ADCREG_CONVERSION_CTRL_BY_TSC2301     0x8000
 +#define TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST                0x0000
 +
 +#define TSC2301_ADCREG_FUNCTION_NONE                  0x0000
 +#define TSC2301_ADCREG_FUNCTION_XY                    0x0400
 +#define TSC2301_ADCREG_FUNCTION_XYZ                   0x0800
 +#define TSC2301_ADCREG_FUNCTION_X                     0x0C00
 +#define TSC2301_ADCREG_FUNCTION_Y                     0x1000
 +#define TSC2301_ADCREG_FUNCTION_Z                     0x1400
 +#define TSC2301_ADCREG_FUNCTION_DAT1                  0x1800
 +#define TSC2301_ADCREG_FUNCTION_DAT2                  0x1C00
 +#define TSC2301_ADCREG_FUNCTION_AUX1                  0x2000
 +#define TSC2301_ADCREG_FUNCTION_AUX2                  0x2400
 +#define TSC2301_ADCREG_FUNCTION_TEMP                  0x2800
 +
 +#define TSC2301_ADCREG_RESOLUTION_8BIT                        0x0100
 +#define TSC2301_ADCREG_RESOLUTION_10BIT                       0x0200
 +#define TSC2301_ADCREG_RESOLUTION_12BIT                       0x0300
 +
 +#define TSC2301_ADCREG_AVERAGING_NONE                 0x0000
 +#define TSC2301_ADCREG_AVERAGING_4AVG                 0x0040
 +#define TSC2301_ADCREG_AVERAGING_8AVG                 0x0080
 +#define TSC2301_ADCREG_AVERAGING_16AVG                        0x00C0
 +
 +#define TSC2301_ADCREG_CLOCK_8MHZ                     0x0000
 +#define TSC2301_ADCREG_CLOCK_4MHZ                     0x0010
 +#define TSC2301_ADCREG_CLOCK_2MHZ                     0x0020
 +#define TSC2301_ADCREG_CLOCK_1MHZ                     0x0030
 +
 +#define TSC2301_ADCREG_VOLTAGE_STAB_0US                       0x0000
 +#define TSC2301_ADCREG_VOLTAGE_STAB_100US             0x0002
 +#define TSC2301_ADCREG_VOLTAGE_STAB_500US             0x0004
 +#define TSC2301_ADCREG_VOLTAGE_STAB_1MS                       0x0006
 +#define TSC2301_ADCREG_VOLTAGE_STAB_5MS                       0x0008
 +#define TSC2301_ADCREG_VOLTAGE_STAB_10MS              0x000A
 +#define TSC2301_ADCREG_VOLTAGE_STAB_50MS              0x000C
 +#define TSC2301_ADCREG_VOLTAGE_STAB_100MS             0x000E
 +
 +#define TSC2301_ADCREG_STOP_CONVERSION                        0x4000
 +
 +#define MAX_12BIT                                     ((1 << 12) - 1)
 +
 +#define TS_RECT_SIZE                                  8
 +#define TSF_MIN_Z1                                    100
 +#define TSF_MAX_Z2                                    4000
 +
 +#define TSF_SAMPLES                                   4
 +
 +struct ts_filter {
 +      int                     sample_cnt;
 +
 +      int                     avg_x;
 +      int                     avg_y;
 +      int                     avg_z1;
 +      int                     avg_z2;
 +};
 +
 +struct ts_coords {
 +      u16                     x;
 +      u16                     y;
 +      u16                     z1;
 +      u16                     z2;
 +};
 +
 +struct tsc2301_ts {
 +      struct input_dev        *idev;
 +      char                    phys[32];
 +      struct timer_list       penup_timer;
 +      struct mutex            mutex;
 +
 +      struct spi_transfer     read_xfer[2];
 +      struct spi_message      read_msg;
 +      struct ts_coords        *coords;
 +
 +      struct ts_filter        filter;
 +
 +      int                     hw_avg_max;
 +      u16                     x;
 +      u16                     y;
 +      u16                     p;
 +
 +      u16                     x_plate_ohm;
 +      int                     stab_time;
 +      int                     max_pressure;
 +      int                     touch_pressure;
 +
 +      u8                      event_sent;
 +      u8                      pen_down;
 +      u8                      disabled;
 +      u8                      disable_depth;
 +
 +      int                     hw_flags;
 +      int                     irq;
 +};
 +
 +
 +static const u16 tsc2301_ts_read_data = 0x8000 | TSC2301_REG_X;
 +
 +static int tsc2301_ts_check_config(struct tsc2301_ts *ts, int *hw_flags)
 +{
 +      int flags;
 +
 +      flags = 0;
 +      switch (ts->hw_avg_max) {
 +      case 0:
 +              flags |= TSC2301_ADCREG_AVERAGING_NONE;
 +              break;
 +      case 4:
 +              flags |= TSC2301_ADCREG_AVERAGING_4AVG;
 +              break;
 +      case 8:
 +              flags |= TSC2301_ADCREG_AVERAGING_8AVG;
 +              break;
 +      case 16:
 +              flags |= TSC2301_ADCREG_AVERAGING_16AVG;
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      switch (ts->stab_time) {
 +      case 0:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_0US;
 +              break;
 +      case 100:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_100US;
 +              break;
 +      case 500:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_500US;
 +              break;
 +      case 1000:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_1MS;
 +              break;
 +      case 5000:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_5MS;
 +              break;
 +      case 10000:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_10MS;
 +              break;
 +      case 50000:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_50MS;
 +              break;
 +      case 100000:
 +              flags |= TSC2301_ADCREG_VOLTAGE_STAB_100MS;
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      *hw_flags = flags;
 +      return 0;
 +}
 +
 +/*
 + * This odd three-time initialization is to work around a bug in TSC2301.
 + * See TSC2301 errata for details.
 + */
 +static int tsc2301_ts_configure(struct tsc2301 *tsc, int flags)
 +{
 +      struct spi_transfer xfer[5];
 +      struct spi_transfer *x;
 +      struct spi_message m;
 +      int i;
 +      u16 val1, val2, val3;
 +      u16 data[10];
 +
 +      val1 = TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST |
 +              TSC2301_ADCREG_STOP_CONVERSION |
 +              TSC2301_ADCREG_FUNCTION_NONE |
 +              TSC2301_ADCREG_RESOLUTION_12BIT |
 +              TSC2301_ADCREG_AVERAGING_NONE |
 +              TSC2301_ADCREG_CLOCK_2MHZ |
 +              TSC2301_ADCREG_VOLTAGE_STAB_100MS;
 +
 +      val2 = TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST |
 +              TSC2301_ADCREG_FUNCTION_XYZ |
 +              TSC2301_ADCREG_RESOLUTION_12BIT |
 +              TSC2301_ADCREG_AVERAGING_16AVG |
 +              TSC2301_ADCREG_CLOCK_1MHZ |
 +              TSC2301_ADCREG_VOLTAGE_STAB_100MS;
 +
 +      /* Averaging and voltage stabilization settings in flags */
 +      val3 = TSC2301_ADCREG_CONVERSION_CTRL_BY_TSC2301 |
 +              TSC2301_ADCREG_FUNCTION_XYZ |
 +              TSC2301_ADCREG_RESOLUTION_12BIT |
 +              TSC2301_ADCREG_CLOCK_2MHZ |
 +              flags;
 +
 +      /* Now we prepare the command for transferring */
 +      data[0] = TSC2301_REG_ADC;
 +      data[1] = val1;
 +      data[2] = TSC2301_REG_ADC;
 +      data[3] = val2;
 +      data[4] = TSC2301_REG_ADC;
 +      data[5] = val3;
 +      data[6] = TSC2301_REG_REF;
 +      data[7] = 1 << 4 | 1 << 2 | 1; /* intref, 100uS settl, 2.5V ref */
 +      data[8] = TSC2301_REG_CONFIG;
 +      data[9] = 3 << 3 | 2 << 0; /* 340uS pre-chrg, 544us delay */
 +
 +      spi_message_init(&m);
 +      m.spi = tsc->spi;
 +
 +      memset(xfer, 0, sizeof(xfer));
 +      x = &xfer[0];
 +
 +      for (i = 0; i < 10; i += 2) {
 +              x->tx_buf = &data[i];
 +              x->len = 4;
 +              if (i != 8)
 +                      x->cs_change = 1;
 +              spi_message_add_tail(x, &m);
 +              x++;
 +      }
 +      spi_sync(m.spi, &m);
 +
 +      return 0;
 +}
 +
 +static void tsc2301_ts_start_scan(struct tsc2301 *tsc)
 +{
 +      tsc2301_ts_configure(tsc, tsc->ts->hw_flags);
 +      tsc2301_kp_restart(tsc);
 +}
 +
 +static void tsc2301_ts_stop_scan(struct tsc2301 *tsc)
 +{
 +      tsc2301_write_reg(tsc, TSC2301_REG_ADC, TSC2301_ADCREG_STOP_CONVERSION);
 +      tsc2301_kp_restart(tsc);
 +}
 +
 +static void update_pen_state(struct tsc2301_ts *ts, int x, int y, int pressure)
 +{
 +      if (pressure) {
 +              input_report_abs(ts->idev, ABS_X, x);
 +              input_report_abs(ts->idev, ABS_Y, y);
 +              input_report_abs(ts->idev, ABS_PRESSURE, pressure);
 +              if (!ts->pen_down)
 +                      input_report_key(ts->idev, BTN_TOUCH, 1);
 +              ts->pen_down = 1;
 +      } else {
 +              input_report_abs(ts->idev, ABS_PRESSURE, 0);
 +              if (ts->pen_down)
 +                      input_report_key(ts->idev, BTN_TOUCH, 0);
 +              ts->pen_down = 0;
 +      }
 +
 +      input_sync(ts->idev);
 +
 +#ifdef VERBOSE
 +      dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
 +#endif
 +}
 +
 +static int filter(struct tsc2301_ts *ts, int x, int y, int z1, int z2)
 +{
 +      int inside_rect, pressure_limit, Rt;
 +      struct ts_filter *tsf = &ts->filter;
 +
 +      /* validate pressure and position */
 +      if (x > MAX_12BIT || y > MAX_12BIT)
 +              return 0;
 +
 +      /* skip coords if the pressure-components are out of range */
 +      if (z1 < TSF_MIN_Z1 || z2 > TSF_MAX_Z2)
 +              return 0;
 +
 +      /* Use the x,y,z1,z2 directly on the first "pen down" event */
 +      if (ts->event_sent) {
 +              tsf->avg_x  += x;
 +              tsf->avg_y  += y;
 +              tsf->avg_z1 += z1;
 +              tsf->avg_z2 += z2;
 +
 +              if (++tsf->sample_cnt < TSF_SAMPLES)
 +                      return 0;
 +              x = tsf->avg_x / TSF_SAMPLES;
 +              y = tsf->avg_y / TSF_SAMPLES;
 +              z1 = tsf->avg_z1 / TSF_SAMPLES;
 +              z2 = tsf->avg_z2 / TSF_SAMPLES;
 +      }
 +      tsf->sample_cnt = 0;
 +      tsf->avg_x  = 0;
 +      tsf->avg_y  = 0;
 +      tsf->avg_z1 = 0;
 +      tsf->avg_z2 = 0;
 +
 +      pressure_limit = ts->event_sent? ts->max_pressure: ts->touch_pressure;
 +
 +      /* z1 is always at least 100: */
 +      Rt = x * (z2 - z1) / z1;
 +      Rt = Rt * ts->x_plate_ohm / 4096;
 +      if (Rt > pressure_limit)
 +              return 0;
 +
 +      /* discard the event if it still is within the previous rect - unless
 +       * if the pressure is harder, but then use previous x,y position */
 +      inside_rect = (
 +          x > (int)ts->x - TS_RECT_SIZE && x < (int)ts->x + TS_RECT_SIZE &&
 +          y > (int)ts->y - TS_RECT_SIZE && y < (int)ts->y + TS_RECT_SIZE);
 +
 +      if (!ts->event_sent || !inside_rect) {
 +              ts->x = x;
 +              ts->y = y;
 +              ts->p = Rt;
 +              return 1;
 +      } else if (Rt < ts->p) {
 +              ts->p = Rt;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +/*
 + * This procedure is called by the SPI framework after the coordinates
 + * have been read from TSC2301
 + */
 +static void tsc2301_ts_rx(void *arg)
 +{
 +      struct tsc2301 *tsc = arg;
 +      struct tsc2301_ts *ts = tsc->ts;
 +      int send_event;
 +      int x, y, z1, z2;
 +
 +      x  = ts->coords->x;
 +      y  = ts->coords->y;
 +      z1 = ts->coords->z1;
 +      z2 = ts->coords->z2;
 +
 +      send_event = filter(ts, x, y, z1, z2);
 +      if (send_event) {
 +              update_pen_state(ts, ts->x, ts->y, ts->p);
 +              ts->event_sent = 1;
 +      }
 +
 +      mod_timer(&ts->penup_timer,
 +                jiffies + msecs_to_jiffies(TSC2301_TS_PENUP_TIME));
 +}
 +
 +/*
 + * Timer is called TSC2301_TS_PENUP_TIME after pen is up
 + */
 +static void tsc2301_ts_timer_handler(unsigned long data)
 +{
 +      struct tsc2301 *tsc = (struct tsc2301 *)data;
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      if (ts->event_sent) {
 +              ts->event_sent = 0;
 +              update_pen_state(ts, 0, 0, 0);
 +      }
 +}
 +
 +/*
 + * This interrupt is called when pen is down and coordinates are
 + * available. That is indicated by a falling edge on DEV line.
 + */
 +static irqreturn_t tsc2301_ts_irq_handler(int irq, void *dev_id)
 +{
 +      struct tsc2301 *tsc = dev_id;
 +      struct tsc2301_ts *ts = tsc->ts;
 +      int r;
 +
 +      r = spi_async(tsc->spi, &ts->read_msg);
 +      if (r)
 +              dev_err(&tsc->spi->dev, "ts: spi_async() failed");
 +
 +      mod_timer(&ts->penup_timer,
 +                jiffies + msecs_to_jiffies(TSC2301_TS_PENUP_TIME));
 +
 +      return IRQ_HANDLED;
 +}
 +
 +static void tsc2301_ts_disable(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      if (ts->disable_depth++ != 0)
 +              return;
 +
 +      disable_irq(ts->irq);
 +
 +      /* wait until penup timer expire normally */
 +      do {
 +              msleep(1);
 +      } while (ts->event_sent);
 +
 +      tsc2301_ts_stop_scan(tsc);
 +}
 +
 +static void tsc2301_ts_enable(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      if (--ts->disable_depth != 0)
 +              return;
 +
 +      enable_irq(ts->irq);
 +
 +      tsc2301_ts_start_scan(tsc);
 +}
 +
 +#ifdef CONFIG_PM
 +int tsc2301_ts_suspend(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      mutex_lock(&ts->mutex);
 +      tsc2301_ts_disable(tsc);
 +      mutex_unlock(&ts->mutex);
 +
 +      return 0;
 +}
 +
 +void tsc2301_ts_resume(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      mutex_lock(&ts->mutex);
 +      tsc2301_ts_enable(tsc);
 +      mutex_unlock(&ts->mutex);
 +}
 +#endif
 +
 +static void tsc2301_ts_setup_spi_xfer(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +      struct spi_message *m = &ts->read_msg;
 +      struct spi_transfer *x = &ts->read_xfer[0];
 +
 +      spi_message_init(m);
 +
 +      x->tx_buf = &tsc2301_ts_read_data;
 +      x->len = 2;
 +      spi_message_add_tail(x, m);
 +
 +      x++;
 +      x->rx_buf = ts->coords;
 +      x->len = 8;
 +      spi_message_add_tail(x, m);
 +
 +      m->complete = tsc2301_ts_rx;
 +      m->context = tsc;
 +}
 +
 +static ssize_t tsc2301_ts_pen_down_show(struct device *dev,
 +                                      struct device_attribute *attr,
 +                                      char *buf)
 +{
 +      struct tsc2301 *tsc = dev_get_drvdata(dev);
 +
 +      return sprintf(buf, "%u\n", tsc->ts->pen_down);
 +}
 +
 +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2301_ts_pen_down_show, NULL);
 +
 +static ssize_t tsc2301_ts_disable_show(struct device *dev,
 +                                     struct device_attribute *attr, char *buf)
 +{
 +      struct tsc2301          *tsc = dev_get_drvdata(dev);
 +      struct tsc2301_ts       *ts = tsc->ts;
 +
 +      return sprintf(buf, "%u\n", ts->disabled);
 +}
 +
 +static ssize_t tsc2301_ts_disable_store(struct device *dev,
 +                                      struct device_attribute *attr,
 +                                      const char *buf, size_t count)
 +{
 +      struct tsc2301          *tsc = dev_get_drvdata(dev);
 +      struct tsc2301_ts       *ts = tsc->ts;
 +      char *endp;
 +      int i;
 +
 +      i = simple_strtoul(buf, &endp, 10);
 +      i = i ? 1 : 0;
 +      mutex_lock(&ts->mutex);
 +      if (i == ts->disabled) goto out;
 +      ts->disabled = i;
 +
 +      if (i)
 +              tsc2301_ts_disable(tsc);
 +      else
 +              tsc2301_ts_enable(tsc);
 +out:
 +      mutex_unlock(&ts->mutex);
 +      return count;
 +}
 +
 +static DEVICE_ATTR(disable_ts, 0664, tsc2301_ts_disable_show,
 +                 tsc2301_ts_disable_store);
 +
 +int __devinit tsc2301_ts_init(struct tsc2301 *tsc,
 +                            struct tsc2301_platform_data *pdata)
 +{
 +      struct tsc2301_ts *ts;
 +      struct input_dev *idev;
 +      int r;
 +      int x_max, y_max;
 +      int x_fudge, y_fudge, p_fudge;
 +
 +      if (pdata->dav_int <= 0) {
 +              dev_err(&tsc->spi->dev, "need DAV IRQ");
 +              return -EINVAL;
 +      }
 +
 +      ts = kzalloc(sizeof(*ts), GFP_KERNEL);
 +      if (ts == NULL)
 +              return -ENOMEM;
 +      tsc->ts = ts;
 +
 +      ts->coords = kzalloc(sizeof(*ts->coords), GFP_KERNEL);
 +      if (ts->coords == NULL) {
 +              kfree(ts);
 +              return -ENOMEM;
 +      }
 +
 +      ts->irq = pdata->dav_int;
 +
 +      init_timer(&ts->penup_timer);
 +      setup_timer(&ts->penup_timer, tsc2301_ts_timer_handler,
 +                      (unsigned long)tsc);
 +
 +      mutex_init(&ts->mutex);
 +
 +      ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
 +      ts->hw_avg_max  = pdata->ts_hw_avg;
 +      ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
 +      ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
 +      ts->stab_time   = pdata->ts_stab_time;
 +
 +      x_max           = pdata->ts_x_max ? : 4096;
 +      y_max           = pdata->ts_y_max ? : 4096;
 +      x_fudge         = pdata->ts_x_fudge ? : 4;
 +      y_fudge         = pdata->ts_y_fudge ? : 8;
 +      p_fudge         = pdata->ts_pressure_fudge ? : 2;
 +
 +      if ((r = tsc2301_ts_check_config(ts, &ts->hw_flags))) {
 +              dev_err(&tsc->spi->dev, "invalid configuration\n");
 +              goto err2;
 +      }
 +
 +      idev = input_allocate_device();
 +      if (idev == NULL) {
 +              r = -ENOMEM;
 +              goto err2;
 +      }
 +      idev->name = "TSC2301 touchscreen";
 +      snprintf(ts->phys, sizeof(ts->phys),
++               "%s/input-ts", dev_name(&tsc->spi->dev));
 +      idev->phys = ts->phys;
 +      idev->dev.parent = &tsc->spi->dev;
 +
 +      idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
 +      idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
 +      ts->idev = idev;
 +
 +      tsc2301_ts_setup_spi_xfer(tsc);
 +
 +      /* These parameters should perhaps be configurable? */
 +      input_set_abs_params(idev, ABS_X, 0, x_max, x_fudge, 0);
 +      input_set_abs_params(idev, ABS_Y, 0, y_max, y_fudge, 0);
 +      input_set_abs_params(idev, ABS_PRESSURE, 0, ts->max_pressure,
 +                           p_fudge, 0);
 +
 +      tsc2301_ts_start_scan(tsc);
 +
 +      r = request_irq(ts->irq, tsc2301_ts_irq_handler,
 +                      IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
 +                      "tsc2301-ts", tsc);
 +      if (r < 0) {
 +              dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
 +              goto err3;
 +      }
 +      set_irq_wake(ts->irq, 1);
 +
 +      if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
 +              goto err4;
 +      if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
 +              goto err5;
 +
 +      r = input_register_device(idev);
 +      if (r < 0) {
 +              dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
 +              goto err6;
 +      }
 +
 +      return 0;
 +err6:
 +      device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
 +err5:
 +      device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
 +err4:
 +      free_irq(ts->irq, tsc);
 +err3:
 +      tsc2301_ts_stop_scan(tsc);
 +      input_free_device(idev);
 +err2:
 +      kfree(ts->coords);
 +      kfree(ts);
 +      return r;
 +}
 +
 +void __devexit tsc2301_ts_exit(struct tsc2301 *tsc)
 +{
 +      struct tsc2301_ts *ts = tsc->ts;
 +
 +      tsc2301_ts_disable(tsc);
 +
 +      device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
 +      device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
 +
 +      free_irq(ts->irq, tsc);
 +      input_unregister_device(ts->idev);
 +
 +      kfree(ts->coords);
 +      kfree(ts);
 +}
 +MODULE_AUTHOR("Jarkko Oikarinen <jarkko.oikarinen@nokia.com>");
 +MODULE_LICENSE("GPL");
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 6b8fb44573d4ae748e56457e01785730a6c153f2,0000000000000000000000000000000000000000..5263b19c81728ca13f00e63d9e3f29cd3d0fc5d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,184 @@@
-       info->mtd.name = pdev->dev.bus_id;
 +/*
 + * drivers/mtd/nand/omap-nand-flash.c
 + *
 + * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
 + * Copyright (c) 2004 David Brownell
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License version 2 as
 + * published by the Free Software Foundation.
 + */
 +
 +#include <linux/init.h>
 +#include <linux/ioport.h>
 +#include <linux/delay.h>
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/platform_device.h>
 +#include <linux/types.h>
 +#include <linux/mtd/mtd.h>
 +#include <linux/mtd/nand.h>
 +#include <linux/mtd/partitions.h>
 +
 +#include <asm/io.h>
 +#include <mach/hardware.h>
 +#include <asm/mach-types.h>
 +#include <asm/mach/flash.h>
 +#include <mach/tc.h>
 +
 +#include <mach/nand.h>
 +
 +#define       DRIVER_NAME     "omapnand"
 +
 +#ifdef CONFIG_MTD_PARTITIONS
 +static const char *part_probes[] = { "cmdlinepart", NULL };
 +#endif
 +
 +struct omap_nand_info {
 +      struct omap_nand_platform_data *pdata;
 +      struct mtd_partition    *parts;
 +      struct mtd_info         mtd;
 +      struct nand_chip        nand;
 +};
 +
 +/*
 + *    hardware specific access to control-lines
 + *    NOTE:  boards may use different bits for these!!
 + *
 + *    ctrl:
 + *    NAND_NCE: bit 0 - don't care
 + *    NAND_CLE: bit 1 -> bit 1  (0x0002)
 + *    NAND_ALE: bit 2 -> bit 2  (0x0004)
 + */
 +
 +static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 +{
 +      struct nand_chip *chip = mtd->priv;
 +      unsigned long mask;
 +
 +      if (cmd == NAND_CMD_NONE)
 +              return;
 +
 +      mask = (ctrl & NAND_CLE) ? 0x02 : 0;
 +      if (ctrl & NAND_ALE)
 +              mask |= 0x04;
 +      writeb(cmd, (unsigned long)chip->IO_ADDR_W | mask);
 +}
 +
 +static int omap_nand_dev_ready(struct mtd_info *mtd)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd);
 +
 +      return info->pdata->dev_ready(info->pdata);
 +}
 +
 +static int __devinit omap_nand_probe(struct platform_device *pdev)
 +{
 +      struct omap_nand_info           *info;
 +      struct omap_nand_platform_data  *pdata = pdev->dev.platform_data;
 +      struct resource                 *res = pdev->resource;
 +      unsigned long                   size = res->end - res->start + 1;
 +      int                             err;
 +
 +      info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
 +      if (!info)
 +              return -ENOMEM;
 +
 +      if (!request_mem_region(res->start, size, pdev->dev.driver->name)) {
 +              err = -EBUSY;
 +              goto out_free_info;
 +      }
 +
 +      info->nand.IO_ADDR_R = ioremap(res->start, size);
 +      if (!info->nand.IO_ADDR_R) {
 +              err = -ENOMEM;
 +              goto out_release_mem_region;
 +      }
 +      info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
 +      info->nand.cmd_ctrl = omap_nand_hwcontrol;
 +      info->nand.ecc.mode = NAND_ECC_SOFT;
 +      info->nand.options = pdata->options;
 +      if (pdata->dev_ready)
 +              info->nand.dev_ready = omap_nand_dev_ready;
 +      else
 +              info->nand.chip_delay = 20;
 +
++      info->mtd.name = dev_name(&pdev->dev);
 +      info->mtd.priv = &info->nand;
 +
 +      info->pdata = pdata;
 +
 +      /* DIP switches on H2 and some other boards change between 8 and 16 bit
 +       * bus widths for flash.  Try the other width if the first try fails.
 +       */
 +      if (nand_scan(&info->mtd, 1)) {
 +              info->nand.options ^= NAND_BUSWIDTH_16;
 +              if (nand_scan(&info->mtd, 1)) {
 +                      err = -ENXIO;
 +                      goto out_iounmap;
 +              }
 +      }
 +      info->mtd.owner = THIS_MODULE;
 +
 +#ifdef CONFIG_MTD_PARTITIONS
 +      err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
 +      if (err > 0)
 +              add_mtd_partitions(&info->mtd, info->parts, err);
 +      else if (err < 0 && pdata->parts)
 +              add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
 +      else
 +#endif
 +              add_mtd_device(&info->mtd);
 +
 +      platform_set_drvdata(pdev, info);
 +
 +      return 0;
 +
 +out_iounmap:
 +      iounmap(info->nand.IO_ADDR_R);
 +out_release_mem_region:
 +      release_mem_region(res->start, size);
 +out_free_info:
 +      kfree(info);
 +
 +      return err;
 +}
 +
 +static int omap_nand_remove(struct platform_device *pdev)
 +{
 +      struct omap_nand_info *info = platform_get_drvdata(pdev);
 +
 +      platform_set_drvdata(pdev, NULL);
 +      /* Release NAND device, its internal structures and partitions */
 +      nand_release(&info->mtd);
 +      iounmap(info->nand.IO_ADDR_R);
 +      kfree(info);
 +      return 0;
 +}
 +
 +static struct platform_driver omap_nand_driver = {
 +      .probe          = omap_nand_probe,
 +      .remove         = omap_nand_remove,
 +      .driver         = {
 +              .name   = DRIVER_NAME,
 +      },
 +};
 +MODULE_ALIAS(DRIVER_NAME);
 +
 +static int __init omap_nand_init(void)
 +{
 +      return platform_driver_register(&omap_nand_driver);
 +}
 +
 +static void __exit omap_nand_exit(void)
 +{
 +      platform_driver_unregister(&omap_nand_driver);
 +}
 +
 +module_init(omap_nand_init);
 +module_exit(omap_nand_exit);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Jian Zhang <jzhang@ti.com> (and others)");
 +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
 +
index 55ba7462cbc9a0563734fade6f9cc669493f0501,0000000000000000000000000000000000000000..516da8f755ae2e6d972aac95582fc5532cea5f08
mode 100644,000000..100644
--- /dev/null
@@@ -1,755 -1,0 +1,755 @@@
-       info->mtd.name          = pdev->dev.bus_id;
 +/*
 + * drivers/mtd/nand/omap2.c
 + *
 + * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
 + * Copyright (c) 2004 Micron Technology Inc.
 + * Copyright (c) 2004 David Brownell
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License version 2 as
 + * published by the Free Software Foundation.
 + */
 +
 +#include <linux/platform_device.h>
 +#include <linux/dma-mapping.h>
 +#include <linux/delay.h>
 +#include <linux/mtd/mtd.h>
 +#include <linux/mtd/nand.h>
 +#include <linux/mtd/partitions.h>
 +#include <linux/io.h>
 +
 +#include <asm/dma.h>
 +
 +#include <mach/gpmc.h>
 +#include <mach/nand.h>
 +
 +#define GPMC_IRQ_STATUS               0x18
 +#define GPMC_ECC_CONFIG               0x1F4
 +#define GPMC_ECC_CONTROL      0x1F8
 +#define GPMC_ECC_SIZE_CONFIG  0x1FC
 +#define GPMC_ECC1_RESULT      0x200
 +
 +#define       DRIVER_NAME     "omap2-nand"
 +#define       NAND_IO_SIZE    SZ_4K
 +
 +#define       NAND_WP_ON      1
 +#define       NAND_WP_OFF     0
 +#define NAND_WP_BIT   0x00000010
 +#define WR_RD_PIN_MONITORING  0x00600000
 +
 +#define       GPMC_BUF_FULL   0x00000001
 +#define       GPMC_BUF_EMPTY  0x00000000
 +
 +#define NAND_Ecc_P1e          (1 << 0)
 +#define NAND_Ecc_P2e          (1 << 1)
 +#define NAND_Ecc_P4e          (1 << 2)
 +#define NAND_Ecc_P8e          (1 << 3)
 +#define NAND_Ecc_P16e         (1 << 4)
 +#define NAND_Ecc_P32e         (1 << 5)
 +#define NAND_Ecc_P64e         (1 << 6)
 +#define NAND_Ecc_P128e                (1 << 7)
 +#define NAND_Ecc_P256e                (1 << 8)
 +#define NAND_Ecc_P512e                (1 << 9)
 +#define NAND_Ecc_P1024e               (1 << 10)
 +#define NAND_Ecc_P2048e               (1 << 11)
 +
 +#define NAND_Ecc_P1o          (1 << 16)
 +#define NAND_Ecc_P2o          (1 << 17)
 +#define NAND_Ecc_P4o          (1 << 18)
 +#define NAND_Ecc_P8o          (1 << 19)
 +#define NAND_Ecc_P16o         (1 << 20)
 +#define NAND_Ecc_P32o         (1 << 21)
 +#define NAND_Ecc_P64o         (1 << 22)
 +#define NAND_Ecc_P128o                (1 << 23)
 +#define NAND_Ecc_P256o                (1 << 24)
 +#define NAND_Ecc_P512o                (1 << 25)
 +#define NAND_Ecc_P1024o               (1 << 26)
 +#define NAND_Ecc_P2048o               (1 << 27)
 +
 +#define TF(value)     (value ? 1 : 0)
 +
 +#define P2048e(a)     (TF(a & NAND_Ecc_P2048e)        << 0)
 +#define P2048o(a)     (TF(a & NAND_Ecc_P2048o)        << 1)
 +#define P1e(a)                (TF(a & NAND_Ecc_P1e)           << 2)
 +#define P1o(a)                (TF(a & NAND_Ecc_P1o)           << 3)
 +#define P2e(a)                (TF(a & NAND_Ecc_P2e)           << 4)
 +#define P2o(a)                (TF(a & NAND_Ecc_P2o)           << 5)
 +#define P4e(a)                (TF(a & NAND_Ecc_P4e)           << 6)
 +#define P4o(a)                (TF(a & NAND_Ecc_P4o)           << 7)
 +
 +#define P8e(a)                (TF(a & NAND_Ecc_P8e)           << 0)
 +#define P8o(a)                (TF(a & NAND_Ecc_P8o)           << 1)
 +#define P16e(a)               (TF(a & NAND_Ecc_P16e)          << 2)
 +#define P16o(a)               (TF(a & NAND_Ecc_P16o)          << 3)
 +#define P32e(a)               (TF(a & NAND_Ecc_P32e)          << 4)
 +#define P32o(a)               (TF(a & NAND_Ecc_P32o)          << 5)
 +#define P64e(a)               (TF(a & NAND_Ecc_P64e)          << 6)
 +#define P64o(a)               (TF(a & NAND_Ecc_P64o)          << 7)
 +
 +#define P128e(a)      (TF(a & NAND_Ecc_P128e)         << 0)
 +#define P128o(a)      (TF(a & NAND_Ecc_P128o)         << 1)
 +#define P256e(a)      (TF(a & NAND_Ecc_P256e)         << 2)
 +#define P256o(a)      (TF(a & NAND_Ecc_P256o)         << 3)
 +#define P512e(a)      (TF(a & NAND_Ecc_P512e)         << 4)
 +#define P512o(a)      (TF(a & NAND_Ecc_P512o)         << 5)
 +#define P1024e(a)     (TF(a & NAND_Ecc_P1024e)        << 6)
 +#define P1024o(a)     (TF(a & NAND_Ecc_P1024o)        << 7)
 +
 +#define P8e_s(a)      (TF(a & NAND_Ecc_P8e)           << 0)
 +#define P8o_s(a)      (TF(a & NAND_Ecc_P8o)           << 1)
 +#define P16e_s(a)     (TF(a & NAND_Ecc_P16e)          << 2)
 +#define P16o_s(a)     (TF(a & NAND_Ecc_P16o)          << 3)
 +#define P1e_s(a)      (TF(a & NAND_Ecc_P1e)           << 4)
 +#define P1o_s(a)      (TF(a & NAND_Ecc_P1o)           << 5)
 +#define P2e_s(a)      (TF(a & NAND_Ecc_P2e)           << 6)
 +#define P2o_s(a)      (TF(a & NAND_Ecc_P2o)           << 7)
 +
 +#define P4e_s(a)      (TF(a & NAND_Ecc_P4e)           << 0)
 +#define P4o_s(a)      (TF(a & NAND_Ecc_P4o)           << 1)
 +
 +#ifdef CONFIG_MTD_PARTITIONS
 +static const char *part_probes[] = { "cmdlinepart", NULL };
 +#endif
 +
 +struct omap_nand_info {
 +      struct nand_hw_control          controller;
 +      struct omap_nand_platform_data  *pdata;
 +      struct mtd_info                 mtd;
 +      struct mtd_partition            *parts;
 +      struct nand_chip                nand;
 +      struct platform_device          *pdev;
 +
 +      int                             gpmc_cs;
 +      unsigned long                   phys_base;
 +      void __iomem                    *gpmc_cs_baseaddr;
 +      void __iomem                    *gpmc_baseaddr;
 +};
 +
 +/*
 + * omap_nand_wp - This function enable or disable the Write Protect feature on
 + * NAND device
 + * @mtd: MTD device structure
 + * @mode: WP ON/OFF
 + */
 +static void omap_nand_wp(struct mtd_info *mtd, int mode)
 +{
 +      struct omap_nand_info *info = container_of(mtd,
 +                                              struct omap_nand_info, mtd);
 +
 +      unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
 +
 +      if (mode)
 +              config &= ~(NAND_WP_BIT);       /* WP is ON */
 +      else
 +              config |= (NAND_WP_BIT);        /* WP is OFF */
 +
 +      __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
 +}
 +
 +/*
 + * hardware specific access to control-lines
 + * NOTE: boards may use different bits for these!!
 + *
 + * ctrl:
 + * NAND_NCE: bit 0 - don't care
 + * NAND_CLE: bit 1 -> Command Latch
 + * NAND_ALE: bit 2 -> Address Latch
 + */
 +static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 +{
 +      struct omap_nand_info *info = container_of(mtd,
 +                                      struct omap_nand_info, mtd);
 +      switch (ctrl) {
 +      case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
 +              info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_COMMAND;
 +              info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_DATA;
 +              break;
 +
 +      case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
 +              info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_ADDRESS;
 +              info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_DATA;
 +              break;
 +
 +      case NAND_CTRL_CHANGE | NAND_NCE:
 +              info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_DATA;
 +              info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_DATA;
 +              break;
 +      }
 +
 +      if (cmd != NAND_CMD_NONE)
 +              __raw_writeb(cmd, info->nand.IO_ADDR_W);
 +}
 +
 +/*
 + * omap_read_buf16 - read data from NAND controller into buffer
 + * @mtd: MTD device structure
 + * @buf: buffer to store date
 + * @len: number of bytes to read
 + */
 +static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
 +{
 +      struct nand_chip *nand = mtd->priv;
 +
 +      __raw_readsw(nand->IO_ADDR_R, buf, len / 2);
 +}
 +
 +/*
 + * omap_write_buf16 - write buffer to NAND controller
 + * @mtd: MTD device structure
 + * @buf: data buffer
 + * @len: number of bytes to write
 + */
 +static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
 +{
 +      struct omap_nand_info *info = container_of(mtd,
 +                                              struct omap_nand_info, mtd);
 +      u16 *p = (u16 *) buf;
 +
 +      /* FIXME try bursts of writesw() or DMA ... */
 +      len >>= 1;
 +
 +      while (len--) {
 +              writew(*p++, info->nand.IO_ADDR_W);
 +
 +              while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
 +                                              GPMC_STATUS) & GPMC_BUF_FULL));
 +      }
 +}
 +/*
 + * omap_verify_buf - Verify chip data against buffer
 + * @mtd: MTD device structure
 + * @buf: buffer containing the data to compare
 + * @len: number of bytes to compare
 + */
 +static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      u16 *p = (u16 *) buf;
 +
 +      len >>= 1;
 +
 +      while (len--) {
 +
 +              if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
 +                      return -EFAULT;
 +      }
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_MTD_NAND_OMAP_HWECC
 +/*
 + * omap_hwecc_init-Initialize the Hardware ECC for NAND flash in GPMC controller
 + * @mtd: MTD device structure
 + */
 +static void omap_hwecc_init(struct mtd_info *mtd)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      register struct nand_chip *chip = mtd->priv;
 +      unsigned long val = 0x0;
 +
 +      /* Read from ECC Control Register */
 +      val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
 +      /* Clear all ECC | Enable Reg1 */
 +      val = ((0x00000001<<8) | 0x00000001);
 +      __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
 +
 +      /* Read from ECC Size Config Register */
 +      val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
 +      /* ECCSIZE1=512 | Select eccResultsize[0-3] */
 +      val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
 +      __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
 +}
 +
 +/*
 + * gen_true_ecc - This function will generate true ECC value, which can be used
 + * when correcting data read from NAND flash memory core
 + * @ecc_buf: buffer to store ecc code
 + */
 +static void gen_true_ecc(u8 *ecc_buf)
 +{
 +      u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
 +              ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
 +
 +      ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
 +                      P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
 +      ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
 +                      P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
 +      ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
 +                      P1e(tmp) | P2048o(tmp) | P2048e(tmp));
 +}
 +
 +/*
 + * omap_compare_ecc - This function compares two ECC's and indicates if there
 + * is an error. If the error can be corrected it will be corrected to the
 + * buffer
 + * @ecc_data1:  ecc code from nand spare area
 + * @ecc_data2:  ecc code from hardware register obtained from hardware ecc
 + * @page_data:  page data
 + */
 +static int omap_compare_ecc(u8 *ecc_data1,    /* read from NAND memory */
 +                          u8 *ecc_data2,      /* read from register */
 +                          u8 *page_data)
 +{
 +      uint    i;
 +      u8      tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
 +      u8      comp0_bit[8], comp1_bit[8], comp2_bit[8];
 +      u8      ecc_bit[24];
 +      u8      ecc_sum = 0;
 +      u8      find_bit = 0;
 +      uint    find_byte = 0;
 +      int     isEccFF;
 +
 +      isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
 +
 +      gen_true_ecc(ecc_data1);
 +      gen_true_ecc(ecc_data2);
 +
 +      for (i = 0; i <= 2; i++) {
 +              *(ecc_data1 + i) = ~(*(ecc_data1 + i));
 +              *(ecc_data2 + i) = ~(*(ecc_data2 + i));
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              tmp0_bit[i]     = *ecc_data1 % 2;
 +              *ecc_data1      = *ecc_data1 / 2;
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              tmp1_bit[i]      = *(ecc_data1 + 1) % 2;
 +              *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              tmp2_bit[i]      = *(ecc_data1 + 2) % 2;
 +              *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              comp0_bit[i]     = *ecc_data2 % 2;
 +              *ecc_data2       = *ecc_data2 / 2;
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              comp1_bit[i]     = *(ecc_data2 + 1) % 2;
 +              *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
 +      }
 +
 +      for (i = 0; i < 8; i++) {
 +              comp2_bit[i]     = *(ecc_data2 + 2) % 2;
 +              *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
 +      }
 +
 +      for (i = 0; i < 6; i++)
 +              ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
 +
 +      for (i = 0; i < 8; i++)
 +              ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
 +
 +      for (i = 0; i < 8; i++)
 +              ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
 +
 +      ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
 +      ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
 +
 +      for (i = 0; i < 24; i++)
 +              ecc_sum += ecc_bit[i];
 +
 +      switch (ecc_sum) {
 +      case 0:
 +              /* Not reached because this function is not called if
 +               *  ECC values are equal
 +               */
 +              return 0;
 +
 +      case 1:
 +              /* Uncorrectable error */
 +              DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
 +              return -1;
 +
 +      case 11:
 +              /* UN-Correctable error */
 +              DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
 +              return -1;
 +
 +      case 12:
 +              /* Correctable error */
 +              find_byte = (ecc_bit[23] << 8) +
 +                          (ecc_bit[21] << 7) +
 +                          (ecc_bit[19] << 6) +
 +                          (ecc_bit[17] << 5) +
 +                          (ecc_bit[15] << 4) +
 +                          (ecc_bit[13] << 3) +
 +                          (ecc_bit[11] << 2) +
 +                          (ecc_bit[9]  << 1) +
 +                          ecc_bit[7];
 +
 +              find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
 +
 +              DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
 +                              "offset: %d, bit: %d\n", find_byte, find_bit);
 +
 +              page_data[find_byte] ^= (1 << find_bit);
 +
 +              return 0;
 +      default:
 +              if (isEccFF) {
 +                      if (ecc_data2[0] == 0 &&
 +                          ecc_data2[1] == 0 &&
 +                          ecc_data2[2] == 0)
 +                              return 0;
 +              }
 +              DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
 +              return -1;
 +      }
 +}
 +
 +/*
 + * omap_correct_data - Compares the ecc read from nand spare area with ECC
 + * registers values and corrects one bit error if it has occured
 + * @mtd: MTD device structure
 + * @dat: page data
 + * @read_ecc: ecc read from nand flash
 + * @calc_ecc: ecc read from ECC registers
 + */
 +static int omap_correct_data(struct mtd_info *mtd, u_char * dat,
 +                              u_char * read_ecc, u_char * calc_ecc)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      int blockCnt = 0, i = 0, ret = 0;
 +
 +      /* Ex NAND_ECC_HW12_2048 */
 +      if ((info->nand.ecc.mode == NAND_ECC_HW) &&
 +                      (info->nand.ecc.size  == 2048))
 +              blockCnt = 4;
 +      else
 +              blockCnt = 1;
 +
 +      for (i = 0; i < blockCnt; i++) {
 +              if (memcmp(read_ecc, calc_ecc, 3) != 0) {
 +                      ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
 +                      if (ret < 0) return ret;
 +              }
 +              read_ecc += 3;
 +              calc_ecc += 3;
 +              dat      += 512;
 +      }
 +      return 0;
 +}
 +
 +/*
 + * omap_calcuate_ecc - Generate non-inverted ECC bytes.
 + * Using noninverted ECC can be considered ugly since writing a blank
 + * page ie. padding will clear the ECC bytes. This is no problem as long
 + * nobody is trying to write data on the seemingly unused page. Reading
 + * an erased page will produce an ECC mismatch between generated and read
 + * ECC bytes that has to be dealt with separately.
 + * @mtd: MTD device structure
 + * @dat: The pointer to data on which ecc is computed
 + * @ecc_code: The ecc_code buffer
 + */
 +static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 +                              u_char *ecc_code)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      unsigned long val = 0x0;
 +      unsigned long reg;
 +
 +      /* Start Reading from HW ECC1_Result = 0x200 */
 +      reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
 +      val = __raw_readl(reg);
 +      *ecc_code++ = val;          /* P128e, ..., P1e */
 +      *ecc_code++ = val >> 16;    /* P128o, ..., P1o */
 +      /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
 +      *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
 +      reg += 4;
 +
 +      return 0;
 +}
 +
 +/*
 + * omap_enable_hwecc - This function enables the hardware ecc functionality
 + * @mtd: MTD device structure
 + * @mode: Read/Write mode
 + */
 +static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      register struct nand_chip *chip = mtd->priv;
 +      unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
 +      unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
 +
 +      switch (mode) {
 +      case NAND_ECC_READ    :
 +              __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
 +              /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
 +              val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
 +              break;
 +      case NAND_ECC_READSYN :
 +               __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
 +              /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
 +              val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
 +              break;
 +      case NAND_ECC_WRITE   :
 +              __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
 +              /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
 +              val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
 +              break;
 +      default:
 +              DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
 +                                      mode);
 +              break;
 +      }
 +
 +      __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
 +}
 +#endif
 +
 +/*
 + * omap_wait - Wait function is called during Program and erase
 + * operations and the way it is called from MTD layer, we should wait
 + * till the NAND chip is ready after the programming/erase operation
 + * has completed.
 + * @mtd: MTD device structure
 + * @chip: NAND Chip structure
 + */
 +static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
 +{
 +      register struct nand_chip *this = mtd->priv;
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      int status = 0;
 +
 +      this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
 +                                              GPMC_CS_NAND_COMMAND;
 +      this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
 +
 +      while (!(status & 0x40)) {
 +               __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
 +              status = __raw_readb(this->IO_ADDR_R);
 +      }
 +      return status;
 +}
 +
 +/*
 + * omap_dev_ready - calls the platform specific dev_ready function
 + * @mtd: MTD device structure
 + */
 +static int omap_dev_ready(struct mtd_info *mtd)
 +{
 +      struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 +                                                      mtd);
 +      unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
 +
 +      if ((val & 0x100) == 0x100) {
 +              /* Clear IRQ Interrupt */
 +              val |= 0x100;
 +              val &= ~(0x0);
 +              __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
 +      } else {
 +              unsigned int cnt = 0;
 +              while (cnt++ < 0x1FF) {
 +                      if  ((val & 0x100) == 0x100)
 +                              return 0;
 +                      val = __raw_readl(info->gpmc_baseaddr +
 +                                                      GPMC_IRQ_STATUS);
 +              }
 +      }
 +
 +      return 1;
 +}
 +
 +static int __devinit omap_nand_probe(struct platform_device *pdev)
 +{
 +      struct omap_nand_info           *info;
 +      struct omap_nand_platform_data  *pdata;
 +      int                             err;
 +      unsigned long                   val;
 +
 +
 +      pdata = pdev->dev.platform_data;
 +      if (pdata == NULL) {
 +              dev_err(&pdev->dev, "platform data missing\n");
 +              return -ENODEV;
 +      }
 +
 +      info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
 +      if (!info) return -ENOMEM;
 +
 +      platform_set_drvdata(pdev, info);
 +
 +      spin_lock_init(&info->controller.lock);
 +      init_waitqueue_head(&info->controller.wq);
 +
 +      info->pdev = pdev;
 +
 +      info->gpmc_cs           = pdata->cs;
 +      info->gpmc_baseaddr     = pdata->gpmc_baseaddr;
 +      info->gpmc_cs_baseaddr  = pdata->gpmc_cs_baseaddr;
 +
 +      info->mtd.priv          = &info->nand;
++      info->mtd.name          = dev_name(&pdev->dev);
 +      info->mtd.owner         = THIS_MODULE;
 +
 +      err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
 +      if (err < 0) {
 +              dev_err(&pdev->dev, "Cannot request GPMC CS\n");
 +              goto out_free_info;
 +      }
 +
 +      /* Enable RD PIN Monitoring Reg */
 +      if (pdata->dev_ready) {
 +              val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
 +              val |= WR_RD_PIN_MONITORING;
 +              gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
 +      }
 +
 +      val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
 +      val &= ~(0xf << 8);
 +      val |=  (0xc & 0xf) << 8;
 +      gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
 +
 +      /* NAND write protect off */
 +      omap_nand_wp(&info->mtd, NAND_WP_OFF);
 +
 +      if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
 +                              pdev->dev.driver->name)) {
 +              err = -EBUSY;
 +              goto out_free_cs;
 +      }
 +
 +      info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
 +      if (!info->nand.IO_ADDR_R) {
 +              err = -ENOMEM;
 +              goto out_release_mem_region;
 +      }
 +      info->nand.controller = &info->controller;
 +
 +      info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
 +      info->nand.cmd_ctrl  = omap_hwcontrol;
 +
 +      /* REVISIT:  only supports 16-bit NAND flash */
 +
 +      info->nand.read_buf   = omap_read_buf16;
 +      info->nand.write_buf  = omap_write_buf16;
 +      info->nand.verify_buf = omap_verify_buf;
 +
 +      /*
 +      * If RDY/BSY line is connected to OMAP then use the omap ready funcrtion
 +      * and the generic nand_wait function which reads the status register
 +      * after monitoring the RDY/BSY line.Otherwise use a standard chip delay
 +      * which is slightly more than tR (AC Timing) of the NAND device and read
 +      * status register until you get a failure or success
 +      */
 +      if (pdata->dev_ready) {
 +              info->nand.dev_ready = omap_dev_ready;
 +              info->nand.chip_delay = 0;
 +      } else {
 +              info->nand.waitfunc = omap_wait;
 +              info->nand.chip_delay = 50;
 +      }
 +
 +      info->nand.options  |= NAND_SKIP_BBTSCAN;
 +      if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
 +                                                              == 0x1000)
 +              info->nand.options  |= NAND_BUSWIDTH_16;
 +
 +#ifdef CONFIG_MTD_NAND_OMAP_HWECC
 +      info->nand.ecc.bytes            = 3;
 +      info->nand.ecc.size             = 512;
 +      info->nand.ecc.calculate        = omap_calculate_ecc;
 +      info->nand.ecc.hwctl            = omap_enable_hwecc;
 +      info->nand.ecc.correct          = omap_correct_data;
 +      info->nand.ecc.mode             = NAND_ECC_HW;
 +
 +      /* init HW ECC */
 +      omap_hwecc_init(&info->mtd);
 +#else
 +      info->nand.ecc.mode = NAND_ECC_SOFT;
 +#endif
 +
 +      /* DIP switches on some boards change between 8 and 16 bit
 +       * bus widths for flash.  Try the other width if the first try fails.
 +       */
 +      if (nand_scan(&info->mtd, 1)) {
 +              info->nand.options ^= NAND_BUSWIDTH_16;
 +              if (nand_scan(&info->mtd, 1)) {
 +                      err = -ENXIO;
 +                      goto out_release_mem_region;
 +              }
 +      }
 +
 +#ifdef CONFIG_MTD_PARTITIONS
 +      err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
 +      if (err > 0)
 +              add_mtd_partitions(&info->mtd, info->parts, err);
 +      else if (pdata->parts)
 +              add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
 +      else
 +#endif
 +              add_mtd_device(&info->mtd);
 +
 +      platform_set_drvdata(pdev, &info->mtd);
 +
 +      return 0;
 +
 +out_release_mem_region:
 +      release_mem_region(info->phys_base, NAND_IO_SIZE);
 +out_free_cs:
 +      gpmc_cs_free(info->gpmc_cs);
 +out_free_info:
 +      kfree(info);
 +
 +      return err;
 +}
 +
 +static int omap_nand_remove(struct platform_device *pdev)
 +{
 +      struct mtd_info *mtd = platform_get_drvdata(pdev);
 +      struct omap_nand_info *info = mtd->priv;
 +
 +      platform_set_drvdata(pdev, NULL);
 +      /* Release NAND device, its internal structures and partitions */
 +      nand_release(&info->mtd);
 +      iounmap(info->nand.IO_ADDR_R);
 +      kfree(&info->mtd);
 +      return 0;
 +}
 +
 +static struct platform_driver omap_nand_driver = {
 +      .probe          = omap_nand_probe,
 +      .remove         = omap_nand_remove,
 +      .driver         = {
 +              .name   = DRIVER_NAME,
 +              .owner  = THIS_MODULE,
 +      },
 +};
 +MODULE_ALIAS(DRIVER_NAME);
 +
 +static int __init omap_nand_init(void)
 +{
 +      printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
 +      return platform_driver_register(&omap_nand_driver);
 +}
 +
 +static void __exit omap_nand_exit(void)
 +{
 +      platform_driver_unregister(&omap_nand_driver);
 +}
 +
 +module_init(omap_nand_init);
 +module_exit(omap_nand_exit);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index a4da278adb0ac5ae01010e44517a6c266bc0f777,48111594ee9bf4921a64a09760b3423840c79d4c..60bdaf1abc4f6f34534962b796af94913e6ae4dc
@@@ -61,8 -60,6 +60,7 @@@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) +
  obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
  obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
  obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
- obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o
 +obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o
  obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
  
  # generic ARP tables