From: Tony Lindgren Date: Fri, 3 Apr 2009 00:10:56 +0000 (-0700) Subject: Merge current mainline tree into linux-omap tree X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?p=linux-2.6-omap-h63xx.git;a=commitdiff_plain;h=0595ee8a05836666b225e6bf003ede0da1e6e329 Merge current mainline tree into linux-omap tree Merge branches 'master' and 'linus' Conflicts: arch/arm/configs/omap_3430sdp_defconfig arch/arm/configs/rx51_defconfig arch/arm/mach-omap2/Kconfig arch/arm/mach-omap2/Makefile arch/arm/mach-omap2/board-2430sdp.c arch/arm/mach-omap2/board-3430sdp.c arch/arm/mach-omap2/board-apollon.c arch/arm/mach-omap2/board-h4.c arch/arm/mach-omap2/board-ldp.c arch/arm/mach-omap2/board-omap3beagle.c arch/arm/mach-omap2/board-omap3pandora.c arch/arm/mach-omap2/board-overo.c arch/arm/mach-omap2/board-rx51-peripherals.c arch/arm/mach-omap2/board-rx51.c arch/arm/mach-omap2/mmc-twl4030.c arch/arm/mach-omap2/mmc-twl4030.h arch/arm/mach-omap2/usb-musb.c arch/arm/plat-omap/dma.c arch/arm/plat-omap/include/mach/board-nokia.h arch/arm/plat-omap/include/mach/irqs.h arch/arm/plat-omap/include/mach/usb.h drivers/Makefile drivers/i2c/chips/Kconfig drivers/video/omap/omapfb_main.c net/ipv4/netfilter/Makefile --- 0595ee8a05836666b225e6bf003ede0da1e6e329 diff --cc arch/arm/plat-omap/include/mach/irqs.h index d12c39fd76c,7f57ee66f36..9499a0520e0 --- a/arch/arm/plat-omap/include/mach/irqs.h +++ b/arch/arm/plat-omap/include/mach/irqs.h @@@ -339,7 -420,9 +420,7 @@@ #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) diff --cc drivers/Makefile index fec4d8e2170,2618a6169a1..3db0a27a914 --- a/drivers/Makefile +++ b/drivers/Makefile @@@ -34,16 -34,10 +34,15 @@@ obj-$(CONFIG_CONNECTOR) += connector obj-$(CONFIG_FB_I810) += video/i810/ obj-$(CONFIG_FB_INTEL) += video/intelfb/ +# we also need input/serio early so serio bus is initialized by the time +# serial drivers start registering their serio ports +obj-$(CONFIG_SERIO) += input/serio/ obj-y += serial/ obj-$(CONFIG_PARPORT) += parport/ - obj-y += base/ block/ misc/ mfd/ net/ media/ + obj-y += base/ block/ misc/ mfd/ media/ +obj-y += i2c/ +obj-y += cbus/ obj-$(CONFIG_NUBUS) += nubus/ - obj-$(CONFIG_ATM) += atm/ obj-y += macintosh/ obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_SCSI) += scsi/ diff --cc drivers/i2c/chips/Kconfig index bf7bd1b9109,8f8c81eb0ae..a01f40778ae --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@@ -64,44 -64,6 +64,31 @@@ config SENSORS_PCA953 This driver is deprecated and will be dropped soon. Use drivers/gpio/pca953x.c instead. - config SENSORS_PCF8591 - tristate "Philips PCF8591" - depends on EXPERIMENTAL - default n - help - If you say yes here you get support for Philips PCF8591 chips. - - This driver can also be built as a module. If so, the module - will be called pcf8591. - - These devices are hard to detect and rarely found on mainstream - hardware. If unsure, say N. - +config TWL4030_MADC + tristate "TWL4030 MADC Driver" + depends on TWL4030_CORE + help + The TWL4030 Monitoring ADC driver enables the host + processor to monitor analog signals using analog-to-digital + conversions on the input source. TWL4030 MADC provides the + following features: + - Single 10-bit ADC with successive approximation register (SAR) conversion; + - Analog multiplexer for 16 inputs; + - Seven (of the 16) inputs are freely available; + - Battery voltage monitoring; + - Concurrent conversion request management; + - Interrupt signal to Primary Interrupt Handler; + - Averaging feature; + - Selective enable/disable of the averaging feature. + + Say 'y' here to statically link this module into the kernel or 'm' + to build it as a dinamically loadable module. The module will be + called twl4030-madc.ko + +config TWL4030_POWEROFF + tristate "TWL4030 device poweroff" + depends on TWL4030_CORE + config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL diff --cc drivers/i2c/chips/Makefile index 8da23eeebe5,55a37603718..0f8a24539a9 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@@ -15,11 -15,7 +15,10 @@@ obj-$(CONFIG_SENSORS_MAX6875) += max687 obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o - obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o +obj-$(CONFIG_TWL4030_POWEROFF) += twl4030-poweroff.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o +obj-$(CONFIG_RTC_X1205_I2C) += x1205.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --cc drivers/input/keyboard/lm8323.c index a796680b785,00000000000..a95ea033300 mode 100644,000000..100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@@ -1,928 -1,0 +1,928 @@@ +/* + * drivers/i2c/chips/lm8323.c + * + * Copyright (C) 2007-2009 Nokia Corporation + * + * Written by Daniel Stone + * Timo O. Karjalainen + * + * Updated by Felipe Balbi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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", client->dev.bus_id); ++ 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 "); +MODULE_AUTHOR("Daniel Stone"); +MODULE_AUTHOR("Felipe Balbi "); +MODULE_DESCRIPTION("LM8323 keypad driver"); +MODULE_LICENSE("GPL"); + diff --cc drivers/input/keyboard/tsc2301_kp.c index a974a5fdc2e,00000000000..0f2cb7f5a10 mode 100644,000000..100644 --- a/drivers/input/keyboard/tsc2301_kp.c +++ b/drivers/input/keyboard/tsc2301_kp.c @@@ -1,475 -1,0 +1,475 @@@ +/* + * TSC2301 keypad driver + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * Written by Jarkko Oikarinen + * Rewritten by 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 +#include +#include +#include +#include +#include +#include + +#include + +#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", tsc->spi->dev.bus_id); ++ 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); +} diff --cc drivers/input/touchscreen/tsc2005.c index 03c3a102dd6,00000000000..ec4c9e7a736 mode 100644,000000..100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@@ -1,728 -1,0 +1,728 @@@ +/* + * TSC2005 touchscreen driver + * + * Copyright (C) 2006-2008 Nokia Corporation + * + * Author: Lauri Leukkunen + * based on TSC2301 driver by Klaus K. Pedersen + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +/** + * 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", - ts->spi->dev.bus_id); ++ 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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tsc2005"); diff --cc drivers/input/touchscreen/tsc2301_ts.c index 6462cc2bf39,00000000000..a157b48dd0b mode 100644,000000..100644 --- a/drivers/input/touchscreen/tsc2301_ts.c +++ b/drivers/input/touchscreen/tsc2301_ts.c @@@ -1,676 -1,0 +1,676 @@@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +/** + * 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", tsc->spi->dev.bus_id); ++ "%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 "); +MODULE_LICENSE("GPL"); diff --cc drivers/mtd/nand/omap-nand-flash.c index 6b8fb44573d,00000000000..5263b19c817 mode 100644,000000..100644 --- a/drivers/mtd/nand/omap-nand-flash.c +++ b/drivers/mtd/nand/omap-nand-flash.c @@@ -1,184 -1,0 +1,184 @@@ +/* + * drivers/mtd/nand/omap-nand-flash.c + * + * Copyright (c) 2004 Texas Instruments, Jian Zhang + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 = pdev->dev.bus_id; ++ 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 (and others)"); +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); + diff --cc drivers/mtd/nand/omap2.c index 55ba7462cbc,00000000000..516da8f755a mode 100644,000000..100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@@ -1,755 -1,0 +1,755 @@@ +/* + * drivers/mtd/nand/omap2.c + * + * Copyright (c) 2004 Texas Instruments, Jian Zhang + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 = pdev->dev.bus_id; ++ 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"); diff --cc net/ipv4/netfilter/Makefile index a4da278adb0,48111594ee9..60bdaf1abc4 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@@ -61,8 -60,6 +60,7 @@@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o +obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o - obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o # generic ARP tables