diff options
Diffstat (limited to 'target/linux/s3c24xx/patches/0033-gta02-acc.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches/0033-gta02-acc.patch.patch | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0033-gta02-acc.patch.patch b/target/linux/s3c24xx/patches/0033-gta02-acc.patch.patch new file mode 100755 index 0000000000..a4683906c3 --- /dev/null +++ b/target/linux/s3c24xx/patches/0033-gta02-acc.patch.patch @@ -0,0 +1,1035 @@ +From 0f43da072e28d0bad639cb6823a7759112ff3489 Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Wed, 16 Jul 2008 14:46:56 +0100 +Subject: [PATCH] gta02-acc.patch + +--- + arch/arm/mach-s3c2410/mach-gta01.c | 2 +- + arch/arm/mach-s3c2410/mach-qt2410.c | 2 +- + arch/arm/mach-s3c2440/mach-gta02.c | 181 +++++++++++++++-- + drivers/input/misc/lis302dl.c | 350 ++++++++++++------------------- + drivers/spi/spi_s3c24xx_gpio.c | 23 ++- + include/asm-arm/arch-s3c2410/spi-gpio.h | 6 +- + include/linux/lis302dl.h | 102 +++++++++ + 7 files changed, 429 insertions(+), 237 deletions(-) + +diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c +index 87bb189..0543daa 100644 +--- a/arch/arm/mach-s3c2410/mach-gta01.c ++++ b/arch/arm/mach-s3c2410/mach-gta01.c +@@ -510,7 +510,7 @@ static struct spi_board_info gta01_spi_board_info[] = { + }, + }; + +-static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs) ++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int csidx, int cs) + { + switch (cs) { + case BITBANG_CS_ACTIVE: +diff --git a/arch/arm/mach-s3c2410/mach-qt2410.c b/arch/arm/mach-s3c2410/mach-qt2410.c +index a1caf4b..6f7b56d 100644 +--- a/arch/arm/mach-s3c2410/mach-qt2410.c ++++ b/arch/arm/mach-s3c2410/mach-qt2410.c +@@ -214,7 +214,7 @@ static struct platform_device qt2410_led = { + + /* SPI */ + +-static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs) ++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int csidx, int cs) + { + switch (cs) { + case BITBANG_CS_ACTIVE: +diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c +index d11da10..3fbb131 100644 +--- a/arch/arm/mach-s3c2440/mach-gta02.c ++++ b/arch/arm/mach-s3c2440/mach-gta02.c +@@ -78,6 +78,9 @@ + + #include <linux/glamofb.h> + ++/* arbitrates which sensor IRQ owns the shared SPI bus */ ++static spinlock_t motion_irq_lock; ++ + static struct map_desc gta02_iodesc[] __initdata = { + { + .virtual = 0xe0000000, +@@ -377,8 +380,6 @@ static struct platform_device *gta02_devices[] __initdata = { + &s3c_device_usbgadget, + &s3c_device_nand, + &s3c_device_ts, +- &s3c_device_spi0, +- &s3c_device_spi1, + >a02_nor_flash, + }; + +@@ -478,10 +479,12 @@ static struct spi_board_info gta02_spi_board_info[] = { + }, + }; + ++#if 0 /* currently this is not used and we use gpio spi */ + static struct glamo_spi_info glamo_spi_cfg = { + .board_size = ARRAY_SIZE(gta02_spi_board_info), + .board_info = gta02_spi_board_info, + }; ++#endif /* 0 */ + + static struct glamo_spigpio_info glamo_spigpio_cfg = { + .pin_clk = GLAMO_GPIO10_OUTPUT, +@@ -507,16 +510,99 @@ static struct platform_device gta01_led_dev = { + + /* SPI: Accelerometers attached to SPI of s3c244x */ + +-static void gta02_spi_acc_set_cs(struct s3c2410_spi_info *spi, int cs, int pol) ++/* ++ * Situation is that Linux SPI can't work in an interrupt context, so we ++ * implement our own bitbang here. Arbitration is needed because not only ++ * can this interrupt happen at any time even if foreground wants to use ++ * the bitbang API from Linux, but multiple motion sensors can be on the ++ * same SPI bus, and multiple interrupts can happen. ++ * ++ * Foreground / interrupt arbitration is okay because the interrupts are ++ * disabled around all the foreground SPI code. ++ * ++ * Interrupt / Interrupt arbitration is evidently needed, otherwise we ++ * lose edge-triggered service after a while due to the two sensors sharing ++ * the SPI bus having irqs at the same time eventually. ++ * ++ * Servicing is typ 75 - 100us at 400MHz. ++ */ ++ ++/* #define DEBUG_SPEW_MS */ ++#define MG_PER_SAMPLE 18 ++ ++void gat02_lis302dl_bitbang_read(struct lis302dl_info *lis) + { +- s3c2410_gpio_setpin(cs, pol); ++ struct lis302dl_platform_data *pdata = lis->pdata; ++ u8 shifter = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */ ++ int n, n1; ++ unsigned long flags; ++#ifdef DEBUG_SPEW_MS ++ s8 x, y, z; ++#endif ++ ++ spin_lock_irqsave(&motion_irq_lock, flags); ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 0); ++ for (n = 0; n < 8; n++) { /* write the r/w, inc and address */ ++ s3c2410_gpio_setpin(pdata->pin_clk, 0); ++ s3c2410_gpio_setpin(pdata->pin_mosi, (shifter >> 7) & 1); ++ s3c2410_gpio_setpin(pdata->pin_clk, 1); ++ shifter <<= 1; ++ } ++ for (n = 0; n < 5; n++) { /* 5 consequetive registers */ ++ for (n1 = 0; n1 < 8; n1++) { /* 8 bits each */ ++ s3c2410_gpio_setpin(pdata->pin_clk, 0); ++ s3c2410_gpio_setpin(pdata->pin_clk, 1); ++ shifter <<= 1; ++ if (s3c2410_gpio_getpin(pdata->pin_miso)) ++ shifter |= 1; ++ } ++ switch (n) { ++ case 0: ++#ifdef DEBUG_SPEW_MS ++ x = shifter; ++#endif ++ input_report_rel(lis->input_dev, REL_X, MG_PER_SAMPLE * (s8)shifter); ++ break; ++ case 2: ++#ifdef DEBUG_SPEW_MS ++ y = shifter; ++#endif ++ input_report_rel(lis->input_dev, REL_Y, MG_PER_SAMPLE * (s8)shifter); ++ break; ++ case 4: ++#ifdef DEBUG_SPEW_MS ++ z = shifter; ++#endif ++ input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE * (s8)shifter); ++ break; ++ } ++ } ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 1); ++ spin_unlock_irqrestore(&motion_irq_lock, flags); ++ input_sync(lis->input_dev); ++#ifdef DEBUG_SPEW_MS ++ printk("%s: %d %d %d\n", pdata->name, x, y, z); ++#endif + } + +-static const struct lis302dl_platform_data lis302_pdata[] = { ++ ++struct lis302dl_platform_data lis302_pdata[] = { + { +- .name = "lis302-1 (top)" ++ .name = "lis302-1 (top)", ++ .pin_chip_select= S3C2410_GPD12, ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, ++ .open_drain = 1, /* altered at runtime by PCB rev */ ++ .lis302dl_bitbang_read = gat02_lis302dl_bitbang_read, + }, { +- .name = "lis302-2 (bottom)" ++ .name = "lis302-2 (bottom)", ++ .pin_chip_select= S3C2410_GPD13, ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, ++ .open_drain = 1, /* altered at runtime by PCB rev */ ++ .lis302dl_bitbang_read = gat02_lis302dl_bitbang_read, + }, + }; + +@@ -525,26 +611,75 @@ static struct spi_board_info gta02_spi_acc_bdinfo[] = { + .modalias = "lis302dl", + .platform_data = &lis302_pdata[0], + .irq = GTA02_IRQ_GSENSOR_1, +- .max_speed_hz = 400 * 1000, ++ .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 1, +- .chip_select = S3C2410_GPD12, ++ .chip_select = 0, + .mode = SPI_MODE_3, + }, + { + .modalias = "lis302dl", + .platform_data = &lis302_pdata[1], + .irq = GTA02_IRQ_GSENSOR_2, +- .max_speed_hz = 400 * 1000, ++ .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 1, +- .chip_select = S3C2410_GPD13, ++ .chip_select = 1, + .mode = SPI_MODE_3, + }, + }; + +-static struct s3c2410_spi_info gta02_spi_acc_cfg = { +- .set_cs = gta02_spi_acc_set_cs, ++static void spi_acc_cs(struct s3c2410_spigpio_info *spigpio_info, ++ int csid, int cs) ++{ ++ struct lis302dl_platform_data * plat_data = ++ (struct lis302dl_platform_data *)spigpio_info-> ++ board_info->platform_data; ++ switch (cs) { ++ case BITBANG_CS_ACTIVE: ++ s3c2410_gpio_setpin(plat_data[csid].pin_chip_select, 0); ++ break; ++ case BITBANG_CS_INACTIVE: ++ s3c2410_gpio_setpin(plat_data[csid].pin_chip_select, 1); ++ break; ++ } ++} ++ ++static struct s3c2410_spigpio_info spi_gpio_cfg = { ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, + .board_size = ARRAY_SIZE(gta02_spi_acc_bdinfo), + .board_info = gta02_spi_acc_bdinfo, ++ .chip_select = &spi_acc_cs, ++ .num_chipselect = 2, ++}; ++ ++static struct resource s3c_spi_acc_resource[] = { ++ [0] = { ++ .start = S3C2410_GPG3, ++ .end = S3C2410_GPG3, ++ }, ++ [1] = { ++ .start = S3C2410_GPG5, ++ .end = S3C2410_GPG5, ++ }, ++ [2] = { ++ .start = S3C2410_GPG6, ++ .end = S3C2410_GPG6, ++ }, ++ [3] = { ++ .start = S3C2410_GPG7, ++ .end = S3C2410_GPG7, ++ }, ++}; ++ ++static struct platform_device s3c_device_spi_acc = { ++ .name = "spi_s3c24xx_gpio", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(s3c_spi_acc_resource), ++ .resource = s3c_spi_acc_resource, ++ .dev = { ++ .platform_data = &spi_gpio_cfg, ++ }, + }; + + static struct resource gta02_led_resources[] = { +@@ -786,10 +921,21 @@ static void __init gta02_machine_init(void) + { + int rc; + ++ switch (system_rev) { ++ case GTA02v6_SYSTEM_REV: ++ /* we need push-pull interrupt from motion sensors */ ++ lis302_pdata[0].open_drain = 0; ++ lis302_pdata[1].open_drain = 0; ++ break; ++ default: ++ break; ++ } ++ ++ spin_lock_init(&motion_irq_lock); ++ + s3c_device_usb.dev.platform_data = >a02_usb_info; + s3c_device_nand.dev.platform_data = >a02_nand_info; + s3c_device_sdi.dev.platform_data = >a02_mmc_cfg; +- s3c_device_spi1.dev.platform_data = >a02_spi_acc_cfg; + + /* Only GTA02v1 has a SD_DETECT GPIO. Since the slot is not + * hot-pluggable, this is not required anyway */ +@@ -801,6 +947,12 @@ static void __init gta02_machine_init(void) + break; + } + ++ /* acc sensor chip selects */ ++ s3c2410_gpio_setpin(S3C2410_GPD12, 1); ++ s3c2410_gpio_cfgpin(S3C2410_GPD12, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_setpin(S3C2410_GPD13, 1); ++ s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPIO_OUTPUT); ++ + INIT_WORK(>a02_udc_vbus_drawer.work, __gta02_udc_vbus_draw); + s3c24xx_udc_set_platdata(>a02_udc_cfg); + set_s3c2410ts_info(>a02_ts_cfg); +@@ -829,6 +981,7 @@ static void __init gta02_machine_init(void) + break; + } + ++ platform_device_register(&s3c_device_spi_acc); + platform_device_register(>a01_button_dev); + platform_device_register(>a01_pm_gsm_dev); + +diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c +index 45c41c8..36bdcf7 100644 +--- a/drivers/input/misc/lis302dl.c ++++ b/drivers/input/misc/lis302dl.c +@@ -33,117 +33,26 @@ + #include <linux/device.h> + #include <linux/platform_device.h> + #include <linux/delay.h> +-#include <linux/input.h> + #include <linux/irq.h> + #include <linux/interrupt.h> + #include <linux/sysfs.h> + + #include <linux/lis302dl.h> + +-#include <linux/spi/spi.h> +- +-#define LIS302DL_WHO_AM_I_MAGIC 0x3b +- +-enum lis302dl_reg { +- LIS302DL_REG_WHO_AM_I = 0x0f, +- LIS302DL_REG_CTRL1 = 0x20, +- LIS302DL_REG_CTRL2 = 0x21, +- LIS302DL_REG_CTRL3 = 0x22, +- LIS302DL_REG_HP_FILTER_RESET = 0x23, +- LIS302DL_REG_STATUS = 0x27, +- LIS302DL_REG_OUT_X = 0x29, +- LIS302DL_REG_OUT_Y = 0x2b, +- LIS302DL_REG_OUT_Z = 0x2d, +- LIS302DL_REG_FF_WU_CFG_1 = 0x30, +- LIS302DL_REG_FF_WU_SRC_1 = 0x31, +- LIS302DL_REG_FF_WU_THS_1 = 0x32, +- LIS302DL_REG_FF_WU_DURATION_1 = 0x33, +- LIS302DL_REG_FF_WU_CFG_2 = 0x34, +- LIS302DL_REG_FF_WU_SRC_2 = 0x35, +- LIS302DL_REG_FF_WU_THS_2 = 0x36, +- LIS302DL_REG_FF_WU_DURATION_2 = 0x37, +- LIS302DL_REG_CLICK_CFG = 0x38, +- LIS302DL_REG_CLICK_SRC = 0x39, +- LIS302DL_REG_CLICK_THSY_X = 0x3b, +- LIS302DL_REG_CLICK_THSZ = 0x3c, +- LIS302DL_REG_CLICK_TIME_LIMIT = 0x3d, +- LIS302DL_REG_CLICK_LATENCY = 0x3e, +- LIS302DL_REG_CLICK_WINDOW = 0x3f, +-}; +- +-enum lis302dl_reg_ctrl1 { +- LIS302DL_CTRL1_Xen = 0x01, +- LIS302DL_CTRL1_Yen = 0x02, +- LIS302DL_CTRL1_Zen = 0x04, +- LIS302DL_CTRL1_STM = 0x08, +- LIS302DL_CTRL1_STP = 0x10, +- LIS302DL_CTRL1_FS = 0x20, +- LIS302DL_CTRL1_PD = 0x40, +- LIS302DL_CTRL1_DR = 0x80, +-}; +- +-enum lis302dl_reg_ctrl3 { +- LIS302DL_CTRL3_PP_OD = 0x40, +-}; +- +-enum lis302dl_reg_status { +- LIS302DL_STATUS_XDA = 0x01, +- LIS302DL_STATUS_YDA = 0x02, +- LIS302DL_STATUS_ZDA = 0x04, +- LIS302DL_STATUS_XYZDA = 0x08, +- LIS302DL_STATUS_XOR = 0x10, +- LIS302DL_STATUS_YOR = 0x20, +- LIS302DL_STATUS_ZOR = 0x40, +- LIS302DL_STATUS_XYZOR = 0x80, +-}; +- +-enum lis302dl_reg_ffwusrc1 { +- LIS302DL_FFWUSRC1_XL = 0x01, +- LIS302DL_FFWUSRC1_XH = 0x02, +- LIS302DL_FFWUSRC1_YL = 0x04, +- LIS302DL_FFWUSRC1_YH = 0x08, +- LIS302DL_FFWUSRC1_ZL = 0x10, +- LIS302DL_FFWUSRC1_ZH = 0x20, +- LIS302DL_FFWUSRC1_IA = 0x40, +-}; +- +-enum lis302dl_reg_cloik_src { +- LIS302DL_CLICKSRC_SINGLE_X = 0x01, +- LIS302DL_CLICKSRC_DOUBLE_X = 0x02, +- LIS302DL_CLICKSRC_SINGLE_Y = 0x04, +- LIS302DL_CLICKSRC_DOUBLE_Y = 0x08, +- LIS302DL_CLICKSRC_SINGLE_Z = 0x10, +- LIS302DL_CLICKSRC_DOUBLE_Z = 0x20, +- LIS302DL_CLICKSRC_IA = 0x40, +-}; +- +-struct lis302dl_info { +- struct spi_device *spi_dev; +- struct input_dev *input_dev; +- struct mutex lock; +- struct work_struct work; +- unsigned int flags; +- unsigned int working; +- u_int8_t regs[0x40]; +-}; +- +-#define LIS302DL_F_WUP_FF 0x0001 /* wake up from free fall */ +-#define LIS302DL_F_WUP_CLICK 0x0002 +-#define LIS302DL_F_POWER 0x0010 +-#define LIS302DL_F_FS 0x0020 /* ADC full scale */ +- + /* lowlevel register access functions */ + +-#define READ_BIT 0x01 +-#define MS_BIT 0x02 +-#define ADDR_SHIFT 2 ++#define READ_BIT 0x80 ++#define READ_BIT_INC_ADS 0xc0 ++#define ADDR_MASK 0x3f + +-static inline u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg) ++static u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg) + { + int rc; + u_int8_t cmd; + +- cmd = (reg << ADDR_SHIFT) | READ_BIT; ++ BUG_ON(reg & ~ADDR_MASK); ++ ++ cmd = reg | READ_BIT; + + rc = spi_w8r8(lis->spi_dev, cmd); + +@@ -161,11 +70,13 @@ static u_int8_t reg_read(struct lis302dl_info *lis, u_int8_t reg) + return ret; + } + +-static inline int __reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val) ++static int __reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val) + { + u_int8_t buf[2]; + +- buf[0] = (reg << ADDR_SHIFT); ++ BUG_ON(reg & ~ADDR_MASK); ++ ++ buf[0] = reg; + buf[1] = val; + + return spi_write(lis->spi_dev, buf, sizeof(buf)); +@@ -207,10 +118,10 @@ static int reg_set_bit_mask(struct lis302dl_info *lis, + enum lis302dl_intmode { + LIS302DL_INTMODE_GND = 0x00, + LIS302DL_INTMODE_FF_WU_1 = 0x01, +- LIX302DL_INTMODE_FF_WU_2 = 0x02, +- LIX302DL_INTMODE_FF_WU_12 = 0x03, +- LIX302DL_INTMODE_DATA_READY = 0x04, +- LIX302DL_INTMODE_CLICK = 0x07, ++ LIS302DL_INTMODE_FF_WU_2 = 0x02, ++ LIS302DL_INTMODE_FF_WU_12 = 0x03, ++ LIS302DL_INTMODE_DATA_READY = 0x04, ++ LIS302DL_INTMODE_CLICK = 0x07, + }; + + static void lis302dl_int_mode(struct spi_device *spi, int int_pin, +@@ -218,12 +129,18 @@ static void lis302dl_int_mode(struct spi_device *spi, int int_pin, + { + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); + +- if (int_pin == 1) ++ switch (int_pin) { ++ case 1: + reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode); +- else if (int_pin == 2) ++ break; ++ case 2: + reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3); ++ break; ++ default: ++ BUG(); ++ } + } +- ++#if 0 + static void _report_btn_single(struct input_dev *inp, int btn) + { + input_report_key(inp, btn, 1); +@@ -241,95 +158,14 @@ static void _report_btn_double(struct input_dev *inp, int btn) + input_sync(inp); + input_report_key(inp, btn, 0); + } ++#endif + +-static void lis302dl_work(struct work_struct *work) +-{ +- struct lis302dl_info *lis = +- container_of(work, struct lis302dl_info, work); +- +- u_int8_t status, ff_wu_src_1, click_src; +- u_int8_t val; +- +- lis->working = 1; +- +- status = reg_read(lis, LIS302DL_REG_STATUS); +- ff_wu_src_1 = reg_read(lis, LIS302DL_REG_FF_WU_SRC_1); +- click_src = reg_read(lis, LIS302DL_REG_CLICK_SRC); +- +- if (status & LIS302DL_STATUS_XDA) { +- val = reg_read(lis, LIS302DL_REG_OUT_X); +- if (lis->flags & LIS302DL_F_FS) +- val = val << 2; +- input_report_rel(lis->input_dev, REL_X, val); +- } +- +- if (status & LIS302DL_STATUS_YDA) { +- val = reg_read(lis, LIS302DL_REG_OUT_Y); +- if (lis->flags & LIS302DL_F_FS) +- val = val << 2; +- input_report_rel(lis->input_dev, REL_Y, val); +- } +- +- if (status & LIS302DL_STATUS_ZDA) { +- val = reg_read(lis, LIS302DL_REG_OUT_Z); +- if (lis->flags & LIS302DL_F_FS) +- val = val << 2; +- input_report_rel(lis->input_dev, REL_Z, val); +- } +- +- if (status & 0xf0) +- dev_dbg(&lis->spi_dev->dev, "overrun!\n"); +- +- /* FIXME: implement overrun statistics */ +- +- if (ff_wu_src_1 & LIS302DL_FFWUSRC1_IA) { +- /* FIXME: free fall interrupt handling */ +- } +- +- if (click_src & LIS302DL_CLICKSRC_IA) { +- if (click_src & LIS302DL_CLICKSRC_SINGLE_X) +- _report_btn_single(lis->input_dev, BTN_X); +- if (click_src & LIS302DL_CLICKSRC_DOUBLE_X) +- _report_btn_double(lis->input_dev, BTN_X); +- +- if (click_src & LIS302DL_CLICKSRC_SINGLE_Y) +- _report_btn_single(lis->input_dev, BTN_Y); +- if (click_src & LIS302DL_CLICKSRC_DOUBLE_Y) +- _report_btn_double(lis->input_dev, BTN_Y); +- +- if (click_src & LIS302DL_CLICKSRC_SINGLE_Z) +- _report_btn_single(lis->input_dev, BTN_Z); +- if (click_src & LIS302DL_CLICKSRC_DOUBLE_Z) +- _report_btn_double(lis->input_dev, BTN_Z); +- } +- +- lis->working = 0; +- input_sync(lis->input_dev); +- put_device(&lis->spi_dev->dev); +- +- enable_irq(lis->spi_dev->irq); +-} +- +-static void lis302dl_schedule_work(struct lis302dl_info *lis) +-{ +- int status; +- +- get_device(&lis->spi_dev->dev); +- status = schedule_work(&lis->work); +- if (!status && !lis->working) +- dev_dbg(&lis->spi_dev->dev, "work item may be lost\n"); +-} + + static irqreturn_t lis302dl_interrupt(int irq, void *_lis) + { + struct lis302dl_info *lis = _lis; + +- lis302dl_schedule_work(lis); +- +- /* Disable any further interrupts until we have processed +- * the current one */ +- disable_irq(lis->spi_dev->irq); +- ++ (lis->pdata->lis302dl_bitbang_read)(lis); + return IRQ_HANDLED; + } + +@@ -388,6 +224,7 @@ static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale); + static struct attribute *lis302dl_sysfs_entries[] = { + &dev_attr_sample_rate.attr, + &dev_attr_full_scale.attr, ++ NULL + }; + + static struct attribute_group lis302dl_attr_group = { +@@ -402,9 +239,16 @@ static int lis302dl_input_open(struct input_dev *inp) + struct lis302dl_info *lis = inp->private; + u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen | + LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen; ++ unsigned long flags; + ++ local_save_flags(flags); + /* make sure we're powered up and generate data ready */ + reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1); ++ local_irq_restore(flags); ++ ++ /* kick it off -- since we are edge triggered, if we missed the edge ++ * permanent low interrupt is death for us */ ++ (lis->pdata->lis302dl_bitbang_read)(lis); + + return 0; + } +@@ -414,9 +258,12 @@ static void lis302dl_input_close(struct input_dev *inp) + struct lis302dl_info *lis = inp->private; + u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen | + LIS302DL_CTRL1_Zen; ++ unsigned long flags; ++ ++ local_save_flags(flags); + + /* since the input core already serializes access and makes sure we +- * only see close() for the close of the lastre user, we can safely ++ * only see close() for the close of the last user, we can safely + * disable the data ready events */ + reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, 0x00); + +@@ -426,6 +273,7 @@ static void lis302dl_input_close(struct input_dev *inp) + reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD, + 0x00); + } ++ local_irq_restore(flags); + } + + static int __devinit lis302dl_probe(struct spi_device *spi) +@@ -433,84 +281,138 @@ static int __devinit lis302dl_probe(struct spi_device *spi) + int rc; + struct lis302dl_info *lis; + u_int8_t wai; ++ unsigned long flags; ++ struct lis302dl_platform_data *pdata; + + lis = kzalloc(sizeof(*lis), GFP_KERNEL); + if (!lis) + return -ENOMEM; + ++ local_save_flags(flags); ++ + mutex_init(&lis->lock); +- INIT_WORK(&lis->work, lis302dl_work); + lis->spi_dev = spi; + + spi_set_drvdata(spi, lis); + ++ pdata = spi->dev.platform_data; ++ + rc = spi_setup(spi); + if (rc < 0) { +- printk(KERN_ERR "error durign spi_setup of lis302dl driver\n"); ++ dev_err(&spi->dev, "error during spi_setup\n"); + dev_set_drvdata(&spi->dev, NULL); +- kfree(lis); +- return rc; ++ goto bail_free_lis; + } + + wai = reg_read(lis, LIS302DL_REG_WHO_AM_I); + if (wai != LIS302DL_WHO_AM_I_MAGIC) { +- printk(KERN_ERR "unknown who_am_i signature 0x%02x\n", wai); ++ dev_err(&spi->dev, "unknown who_am_i signature 0x%02x\n", wai); + dev_set_drvdata(&spi->dev, NULL); +- kfree(lis); +- return -ENODEV; +- } +- +- /* switch interrupt to open collector */ +- reg_write(lis, LIS302DL_CTRL3_PP_OD, 0x7c); +- +- rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt, IRQF_DISABLED, +- "lis302dl", NULL); +- if (rc < 0) { +- dev_err(&spi->dev, "error requesting IRQ %d\n", +- lis->spi_dev->irq); +- /* FIXME */ +- return rc; ++ rc = -ENODEV; ++ goto bail_free_lis; + } + + rc = sysfs_create_group(&spi->dev.kobj, &lis302dl_attr_group); + if (rc) { + dev_err(&spi->dev, "error creating sysfs group\n"); +- /* FIXME */ +- return rc; ++ goto bail_free_lis; + } + + /* initialize input layer details */ + lis->input_dev = input_allocate_device(); + if (!lis->input_dev) { + dev_err(&spi->dev, "Unable to allocate input device\n"); +- /* FIXME */ ++ goto bail_sysfs; + } + + set_bit(EV_REL, lis->input_dev->evbit); +- set_bit(EV_KEY, lis->input_dev->evbit); ++ set_bit(REL_X, lis->input_dev->relbit); ++ set_bit(REL_Y, lis->input_dev->relbit); ++ set_bit(REL_Z, lis->input_dev->relbit); ++/* set_bit(EV_KEY, lis->input_dev->evbit); + set_bit(BTN_X, lis->input_dev->keybit); + set_bit(BTN_Y, lis->input_dev->keybit); + set_bit(BTN_Z, lis->input_dev->keybit); +- ++*/ + lis->input_dev->private = lis; +- lis->input_dev->name = "lis302dl"; /* FIXME: platform data */ +- lis->input_dev->id.bustype = BUS_I2C; /* FIXME: SPI Bus */ ++ lis->input_dev->name = pdata->name; ++ /* SPI Bus not defined as a valid bus for input subsystem*/ ++ lis->input_dev->id.bustype = BUS_I2C; /* lie about it */ + lis->input_dev->open = lis302dl_input_open; + lis->input_dev->close = lis302dl_input_close; + +- input_register_device(lis->input_dev); ++ rc = input_register_device(lis->input_dev); ++ if (rc) { ++ dev_err(&spi->dev, "error %d registering input device\n", rc); ++ goto bail_inp_dev; ++ } ++ ++ reg_write(lis, LIS302DL_REG_CTRL1, 0x47); ++ reg_write(lis, LIS302DL_REG_CTRL3, 0xc0); ++ reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x14); ++ reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00); ++ reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x95); ++ ++ reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_Xen | ++ LIS302DL_CTRL1_Yen | ++ LIS302DL_CTRL1_Zen); ++ ++ if (pdata->open_drain) ++ /* switch interrupt to open collector, active-low */ ++ reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_PP_OD | ++ LIS302DL_CTRL3_IHL); ++ else ++ /* push-pull, active-low */ ++ reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_IHL); ++ ++ lis302dl_int_mode(spi, 1, LIS302DL_INTMODE_DATA_READY); ++ lis302dl_int_mode(spi, 2, LIS302DL_INTMODE_DATA_READY); ++ ++ reg_read(lis, LIS302DL_REG_STATUS); ++ reg_read(lis, LIS302DL_REG_FF_WU_SRC_1); ++ reg_read(lis, LIS302DL_REG_FF_WU_SRC_2); ++ reg_read(lis, LIS302DL_REG_CLICK_SRC); + ++ dev_info(&spi->dev, "Found %s\n", pdata->name); ++ ++ lis->pdata = pdata; ++ ++ rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt, ++ IRQF_TRIGGER_FALLING, "lis302dl", lis); ++ if (rc < 0) { ++ dev_err(&spi->dev, "error requesting IRQ %d\n", ++ lis->spi_dev->irq); ++ goto bail_inp_reg; ++ } ++ local_irq_restore(flags); + return 0; ++ ++bail_inp_reg: ++ input_unregister_device(lis->input_dev); ++bail_inp_dev: ++ input_free_device(lis->input_dev); ++bail_sysfs: ++ sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group); ++bail_free_lis: ++ kfree(lis); ++ local_irq_restore(flags); ++ return rc; + } + + static int __devexit lis302dl_remove(struct spi_device *spi) + { + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); ++ unsigned long flags; + + /* power down the device */ ++ local_save_flags(flags); + reg_write(lis, LIS302DL_REG_CTRL1, 0x00); ++ local_irq_restore(flags); ++ + sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group); + input_unregister_device(lis->input_dev); ++ if (lis->input_dev) ++ input_free_device(lis->input_dev); + dev_set_drvdata(&spi->dev, NULL); + kfree(lis); + +@@ -521,6 +423,10 @@ static int __devexit lis302dl_remove(struct spi_device *spi) + static int lis302dl_suspend(struct spi_device *spi, pm_message_t state) + { + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); ++ unsigned long flags; ++ ++ disable_irq(lis->spi_dev->irq); ++ local_save_flags(flags); + + /* save registers */ + lis->regs[LIS302DL_REG_CTRL1] = reg_read(lis, LIS302DL_REG_CTRL1); +@@ -561,12 +467,17 @@ static int lis302dl_suspend(struct spi_device *spi, pm_message_t state) + reg_write(lis, LIS302DL_REG_CTRL1, tmp); + } + ++ local_irq_restore(flags); ++ + return 0; + } + + static int lis302dl_resume(struct spi_device *spi) + { + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); ++ unsigned long flags; ++ ++ local_save_flags(flags); + + /* restore registers after resume */ + reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1]); +@@ -597,6 +508,9 @@ static int lis302dl_resume(struct spi_device *spi) + reg_write(lis, LIS302DL_REG_CLICK_WINDOW, + lis->regs[LIS302DL_REG_CLICK_WINDOW]); + ++ local_irq_restore(flags); ++ enable_irq(lis->spi_dev->irq); ++ + return 0; + } + #else +diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c +index e33f614..ccadf35 100644 +--- a/drivers/spi/spi_s3c24xx_gpio.c ++++ b/drivers/spi/spi_s3c24xx_gpio.c +@@ -91,7 +91,7 @@ static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value) + struct s3c2410_spigpio *sg = spidev_to_sg(dev); + + if (sg->info && sg->info->chip_select) +- (sg->info->chip_select)(sg->info, value); ++ (sg->info->chip_select)(sg->info, dev->chip_select, value); + } + + static int s3c2410_spigpio_probe(struct platform_device *dev) +@@ -100,6 +100,7 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) + struct spi_master *master; + struct s3c2410_spigpio *sp; + int ret; ++ int i; + + master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio)); + if (master == NULL) { +@@ -112,9 +113,11 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) + + platform_set_drvdata(dev, sp); + +- /* copy in the plkatform data */ ++ /* copy in the platform data */ + info = sp->info = dev->dev.platform_data; + ++ master->num_chipselect = info->num_chipselect; ++ + /* setup spi bitbang adaptor */ + sp->bitbang.master = spi_master_get(master); + sp->bitbang.master->bus_num = info->bus_num; +@@ -142,6 +145,22 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) + if (ret) + goto err_no_bitbang; + ++ /* register the chips to go with the board */ ++ ++ for (i = 0; i < sp->info->board_size; i++) { ++ struct spi_device *spidev; ++ ++ dev_info(&dev->dev, "registering %p: %s\n", ++ &sp->info->board_info[i], ++ sp->info->board_info[i].modalias); ++ ++ sp->info->board_info[i].controller_data = sp; ++ spidev = spi_new_device(master, sp->info->board_info + i); ++ if (spidev) ++ spidev->max_speed_hz = ++ sp->info->board_info[i].max_speed_hz; ++ } ++ + return 0; + + err_no_bitbang: +diff --git a/include/asm-arm/arch-s3c2410/spi-gpio.h b/include/asm-arm/arch-s3c2410/spi-gpio.h +index 7380373..d4efc97 100644 +--- a/include/asm-arm/arch-s3c2410/spi-gpio.h ++++ b/include/asm-arm/arch-s3c2410/spi-gpio.h +@@ -19,8 +19,12 @@ struct s3c2410_spigpio_info { + unsigned long pin_miso; + + int bus_num; ++ int num_chipselect; + +- void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs); ++ unsigned long board_size; ++ struct spi_board_info *board_info; ++ ++ void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs); + }; + + +diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h +index d0f31be..2dea813 100644 +--- a/include/linux/lis302dl.h ++++ b/include/linux/lis302dl.h +@@ -2,10 +2,112 @@ + #define _LINUX_LIS302DL_H + + #include <linux/types.h> ++#include <linux/spi/spi.h> ++#include <linux/input.h> ++ ++ ++struct lis302dl_info; + + struct lis302dl_platform_data { + char *name; ++ unsigned long pin_chip_select; ++ unsigned long pin_clk; ++ unsigned long pin_mosi; ++ unsigned long pin_miso; ++ int open_drain; ++ void (*lis302dl_bitbang_read)(struct lis302dl_info *); ++}; ++ ++struct lis302dl_info { ++ struct lis302dl_platform_data *pdata; ++ struct spi_device *spi_dev; ++ struct input_dev *input_dev; ++ struct mutex lock; ++ unsigned int flags; ++ u_int8_t regs[0x40]; ++}; ++ ++enum lis302dl_reg { ++ LIS302DL_REG_WHO_AM_I = 0x0f, ++ LIS302DL_REG_CTRL1 = 0x20, ++ LIS302DL_REG_CTRL2 = 0x21, ++ LIS302DL_REG_CTRL3 = 0x22, ++ LIS302DL_REG_HP_FILTER_RESET = 0x23, ++ LIS302DL_REG_STATUS = 0x27, ++ LIS302DL_REG_OUT_X = 0x29, ++ LIS302DL_REG_OUT_Y = 0x2b, ++ LIS302DL_REG_OUT_Z = 0x2d, ++ LIS302DL_REG_FF_WU_CFG_1 = 0x30, ++ LIS302DL_REG_FF_WU_SRC_1 = 0x31, ++ LIS302DL_REG_FF_WU_THS_1 = 0x32, ++ LIS302DL_REG_FF_WU_DURATION_1 = 0x33, ++ LIS302DL_REG_FF_WU_CFG_2 = 0x34, ++ LIS302DL_REG_FF_WU_SRC_2 = 0x35, ++ LIS302DL_REG_FF_WU_THS_2 = 0x36, ++ LIS302DL_REG_FF_WU_DURATION_2 = 0x37, ++ LIS302DL_REG_CLICK_CFG = 0x38, ++ LIS302DL_REG_CLICK_SRC = 0x39, ++ LIS302DL_REG_CLICK_THSY_X = 0x3b, ++ LIS302DL_REG_CLICK_THSZ = 0x3c, ++ LIS302DL_REG_CLICK_TIME_LIMIT = 0x3d, ++ LIS302DL_REG_CLICK_LATENCY = 0x3e, ++ LIS302DL_REG_CLICK_WINDOW = 0x3f, ++}; ++ ++enum lis302dl_reg_ctrl1 { ++ LIS302DL_CTRL1_Xen = 0x01, ++ LIS302DL_CTRL1_Yen = 0x02, ++ LIS302DL_CTRL1_Zen = 0x04, ++ LIS302DL_CTRL1_STM = 0x08, ++ LIS302DL_CTRL1_STP = 0x10, ++ LIS302DL_CTRL1_FS = 0x20, ++ LIS302DL_CTRL1_PD = 0x40, ++ LIS302DL_CTRL1_DR = 0x80, ++}; ++ ++enum lis302dl_reg_ctrl3 { ++ LIS302DL_CTRL3_PP_OD = 0x40, ++ LIS302DL_CTRL3_IHL = 0x80, + }; + ++enum lis302dl_reg_status { ++ LIS302DL_STATUS_XDA = 0x01, ++ LIS302DL_STATUS_YDA = 0x02, ++ LIS302DL_STATUS_ZDA = 0x04, ++ LIS302DL_STATUS_XYZDA = 0x08, ++ LIS302DL_STATUS_XOR = 0x10, ++ LIS302DL_STATUS_YOR = 0x20, ++ LIS302DL_STATUS_ZOR = 0x40, ++ LIS302DL_STATUS_XYZOR = 0x80, ++}; ++ ++enum lis302dl_reg_ffwusrc1 { ++ LIS302DL_FFWUSRC1_XL = 0x01, ++ LIS302DL_FFWUSRC1_XH = 0x02, ++ LIS302DL_FFWUSRC1_YL = 0x04, ++ LIS302DL_FFWUSRC1_YH = 0x08, ++ LIS302DL_FFWUSRC1_ZL = 0x10, ++ LIS302DL_FFWUSRC1_ZH = 0x20, ++ LIS302DL_FFWUSRC1_IA = 0x40, ++}; ++ ++enum lis302dl_reg_cloik_src { ++ LIS302DL_CLICKSRC_SINGLE_X = 0x01, ++ LIS302DL_CLICKSRC_DOUBLE_X = 0x02, ++ LIS302DL_CLICKSRC_SINGLE_Y = 0x04, ++ LIS302DL_CLICKSRC_DOUBLE_Y = 0x08, ++ LIS302DL_CLICKSRC_SINGLE_Z = 0x10, ++ LIS302DL_CLICKSRC_DOUBLE_Z = 0x20, ++ LIS302DL_CLICKSRC_IA = 0x40, ++}; ++ ++#define LIS302DL_WHO_AM_I_MAGIC 0x3b ++ ++#define LIS302DL_F_WUP_FF 0x0001 /* wake up from free fall */ ++#define LIS302DL_F_WUP_CLICK 0x0002 ++#define LIS302DL_F_POWER 0x0010 ++#define LIS302DL_F_FS 0x0020 /* ADC full scale */ ++ ++ + #endif /* _LINUX_LIS302DL_H */ + +-- +1.5.6.3 + |