diff options
author | mokopatches <mokopatches@openmoko.org> | 2008-11-19 17:03:16 +0000 |
---|---|---|
committer | warmcat <andy@warmcat.com> | 2008-11-19 17:03:16 +0000 |
commit | 97b917eaa7396335ec12cbb2927eafbe0f5b3489 (patch) | |
tree | ba4981fc414e5db41b6507d73249d277e1287557 | |
parent | a65837dce6a2d4061ebfccfb6a8e4234b5c98ef5 (diff) |
gta02-acc.patch
-rw-r--r-- | arch/arm/mach-s3c2410/mach-gta01.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/mach-qt2410.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c2440/mach-gta02.c | 181 | ||||
-rw-r--r-- | drivers/input/misc/lis302dl.c | 350 | ||||
-rw-r--r-- | drivers/spi/spi_s3c24xx_gpio.c | 23 | ||||
-rw-r--r-- | include/asm-arm/arch-s3c2410/spi-gpio.h | 31 | ||||
-rw-r--r-- | include/linux/lis302dl.h | 102 |
7 files changed, 455 insertions, 236 deletions
diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c index 87bb1899a67..0543daa937b 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 9678a53ceeb..4540c7acd65 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 d11da106b66..3fbb131d6b0 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 45c41c81719..36bdcf79b02 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 cc1f647f579..b41de422cb4 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 new file mode 100644 index 00000000000..d4efc973f18 --- /dev/null +++ b/include/asm-arm/arch-s3c2410/spi-gpio.h @@ -0,0 +1,31 @@ +/* linux/include/asm-arm/arch-s3c2410/spi-gpio.h + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 - SPI Controller platfrom_device info + * + * 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. +*/ + +#ifndef __ASM_ARCH_SPIGPIO_H +#define __ASM_ARCH_SPIGPIO_H __FILE__ + +struct s3c2410_spigpio_info { + unsigned long pin_clk; + unsigned long pin_mosi; + unsigned long pin_miso; + + int bus_num; + int num_chipselect; + + unsigned long board_size; + struct spi_board_info *board_info; + + void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs); +}; + + +#endif /* __ASM_ARCH_SPIGPIO_H */ diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h index d0f31be9ee8..2dea8130a24 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 */ |