#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)
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/
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
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
--- /dev/null
- 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");
+
--- /dev/null
- 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);
+}
--- /dev/null
- 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");
--- /dev/null
- "%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");
--- /dev/null
- 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");
+
--- /dev/null
- 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");
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