From e866878cd2a68e7ef46241f4317eddabb2eb359e Mon Sep 17 00:00:00 2001 From: Balaji Rao Date: Thu, 26 Feb 2009 04:10:52 +0000 Subject: Subject: lis302dl_use_s3c24xx_gpio_spi_bitbang.patch X-Git-Url: http://git.openmoko.org/?p=kernel.git;a=commitdiff_plain;h=5689ad9b0ded29adec4be64df53e488c648b9831 lis302dl_use_s3c24xx_gpio_spi_bitbang.patch Change lis302dl driver to use generic spi code. This requires that we define a s3c24xx_gpio spi bitbang based controller in mach-gta02.c. Signed-off-by: Balaji Rao --- arch/arm/mach-s3c2442/mach-gta02.c | 214 +++++++++++++------------------------ drivers/input/misc/lis302dl.c | 102 +++++++++++++----- include/linux/lis302dl.h | 9 +- 3 files changed, 158 insertions(+), 167 deletions(-) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 26910a78721..55f9d946d4a 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -76,6 +76,7 @@ #include #include #include +#include #include #include @@ -1104,124 +1105,7 @@ static struct glamo_spigpio_info glamo_spigpio_cfg = { .board_info = gta02_spi_board_info, }; -/* SPI: Accelerometers attached to SPI of s3c244x */ - -/* - * 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 - -struct lis302dl_platform_data lis302_pdata_top; -struct lis302dl_platform_data lis302_pdata_bottom; - -/* - * generic SPI RX and TX bitbang - * only call with interrupts off! - */ - -static void __gta02_lis302dl_bitbang(struct lis302dl_info *lis, u8 *tx, - int tx_bytes, u8 *rx, int rx_bytes) -{ - struct lis302dl_platform_data *pdata = lis->pdata; - int n; - u8 shifter = 0; - unsigned long other_cs; - - /* - * Huh... "quirk"... CS on this device is not really "CS" like you can - * expect. - * - * When it is 0 it selects SPI interface mode. - * When it is 1 it selects I2C interface mode. - * - * Because we have 2 devices on one interface we have to make sure - * that the "disabled" device (actually in I2C mode) don't think we're - * talking to it. - * - * When we talk to the "enabled" device, the "disabled" device sees - * the clocks as I2C clocks, creating havoc. - * - * I2C sees MOSI going LOW while CLK HIGH as a START action, thus we - * must ensure this is never issued. - */ - - if (&lis302_pdata_top == pdata) - other_cs = lis302_pdata_bottom.pin_chip_select; - else - other_cs = lis302_pdata_top.pin_chip_select; - - s3c2410_gpio_setpin(other_cs, 1); - s3c2410_gpio_setpin(pdata->pin_chip_select, 1); - s3c2410_gpio_setpin(pdata->pin_clk, 1); - s3c2410_gpio_setpin(pdata->pin_chip_select, 0); - - /* send the register index, r/w and autoinc bits */ - for (n = 0; n < (tx_bytes << 3); n++) { - if (!(n & 7)) - shifter = ~tx[n >> 3]; - s3c2410_gpio_setpin(pdata->pin_clk, 0); - s3c2410_gpio_setpin(pdata->pin_mosi, !(shifter & 0x80)); - s3c2410_gpio_setpin(pdata->pin_clk, 1); - shifter <<= 1; - } - - for (n = 0; n < (rx_bytes << 3); n++) { /* 8 bits each */ - s3c2410_gpio_setpin(pdata->pin_clk, 0); - shifter <<= 1; - if (s3c2410_gpio_getpin(pdata->pin_miso)) - shifter |= 1; - if ((n & 7) == 7) - rx[n >> 3] = shifter; - s3c2410_gpio_setpin(pdata->pin_clk, 1); - } - s3c2410_gpio_setpin(pdata->pin_chip_select, 1); - s3c2410_gpio_setpin(other_cs, 1); -} - - -static int gta02_lis302dl_bitbang_read_reg(struct lis302dl_info *lis, u8 reg) -{ - u8 data = 0xc0 | reg; /* read, autoincrement */ - unsigned long flags; - - local_irq_save(flags); - - __gta02_lis302dl_bitbang(lis, &data, 1, &data, 1); - - local_irq_restore(flags); - - return data; -} - -static void gta02_lis302dl_bitbang_write_reg(struct lis302dl_info *lis, u8 reg, - u8 val) -{ - u8 data[2] = { 0x00 | reg, val }; /* write, no autoincrement */ - unsigned long flags; - - local_irq_save(flags); - - __gta02_lis302dl_bitbang(lis, &data[0], 2, NULL, 0); - - local_irq_restore(flags); - -} - +/*----------- SPI: Accelerometers attached to SPI of s3c244x ----------------- */ void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume) { @@ -1253,8 +1137,6 @@ void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume) } - - struct lis302dl_platform_data lis302_pdata_top = { .name = "lis302-1 (top)", .pin_chip_select= S3C2410_GPD12, @@ -1263,9 +1145,6 @@ struct lis302dl_platform_data lis302_pdata_top = { .pin_miso = S3C2410_GPG5, .interrupt = GTA02_IRQ_GSENSOR_1, .open_drain = 1, /* altered at runtime by PCB rev */ - .lis302dl_bitbang = __gta02_lis302dl_bitbang, - .lis302dl_bitbang_reg_read = gta02_lis302dl_bitbang_read_reg, - .lis302dl_bitbang_reg_write = gta02_lis302dl_bitbang_write_reg, .lis302dl_suspend_io = gta02_lis302dl_suspend_io, }; @@ -1277,29 +1156,91 @@ struct lis302dl_platform_data lis302_pdata_bottom = { .pin_miso = S3C2410_GPG5, .interrupt = GTA02_IRQ_GSENSOR_2, .open_drain = 1, /* altered at runtime by PCB rev */ - .lis302dl_bitbang = __gta02_lis302dl_bitbang, - .lis302dl_bitbang_reg_read = gta02_lis302dl_bitbang_read_reg, - .lis302dl_bitbang_reg_write = gta02_lis302dl_bitbang_write_reg, .lis302dl_suspend_io = gta02_lis302dl_suspend_io, }; +static struct spi_board_info lis302dl_spi_board_info[] = { + { + .modalias = "lis302dl", + /* platform_data */ + .platform_data = &lis302_pdata_top, + /* controller_data */ + /* irq */ + .max_speed_hz = 100 * 1000, + .bus_num = 3, + .chip_select = 0, + }, -static struct platform_device s3c_device_spi_acc1 = { - .name = "lis302dl", - .id = 1, - .dev = { - .platform_data = &lis302_pdata_top, + { + .modalias = "lis302dl", + /* platform_data */ + .platform_data = &lis302_pdata_bottom, + /* controller_data */ + /* irq */ + .max_speed_hz = 100 * 1000, + .bus_num = 3, + .chip_select = 1, }, }; -static struct platform_device s3c_device_spi_acc2 = { - .name = "lis302dl", - .id = 2, +static void gta02_lis302_chip_select(struct s3c2410_spigpio_info *info, int csid, int cs) +{ + + /* + * Huh... "quirk"... CS on this device is not really "CS" like you can + * expect. + * + * When it is 0 it selects SPI interface mode. + * When it is 1 it selects I2C interface mode. + * + * Because we have 2 devices on one interface we have to make sure + * that the "disabled" device (actually in I2C mode) don't think we're + * talking to it. + * + * When we talk to the "enabled" device, the "disabled" device sees + * the clocks as I2C clocks, creating havoc. + * + * I2C sees MOSI going LOW while CLK HIGH as a START action, thus we + * must ensure this is never issued. + */ + + int cs_gpio, other_cs_gpio; + + cs_gpio = csid ? S3C2410_GPD13 : S3C2410_GPD12; + other_cs_gpio = (1 - csid) ? S3C2410_GPD13 : S3C2410_GPD12; + + + if (cs == BITBANG_CS_ACTIVE) { + s3c2410_gpio_setpin(other_cs_gpio, 1); + s3c2410_gpio_setpin(cs_gpio, 1); + s3c2410_gpio_setpin(info->pin_clk, 1); + s3c2410_gpio_setpin(cs_gpio, 0); + } else { + s3c2410_gpio_setpin(cs_gpio, 1); + s3c2410_gpio_setpin(other_cs_gpio, 1); + } +} + +static struct s3c2410_spigpio_info gta02_spigpio_cfg = { + .pin_clk = S3C2410_GPG7, + .pin_mosi = S3C2410_GPG6, + .pin_miso = S3C2410_GPG5, + .board_size = ARRAY_SIZE(lis302dl_spi_board_info), + .board_info = lis302dl_spi_board_info, + .num_chipselect = 2, + .chip_select = gta02_lis302_chip_select, + .non_blocking_transfer = 1, +}; + +static struct platform_device gta02_spi_gpio_dev = { + .name = "spi_s3c24xx_gpio", .dev = { - .platform_data = &lis302_pdata_bottom, + .platform_data = >a02_spigpio_cfg, }, }; +/*----------- / SPI: Accelerometers attached to SPI of s3c244x ----------------- */ + static struct resource gta02_led_resources[] = { { .name = "gta02-power:orange", @@ -1576,8 +1517,7 @@ static struct platform_device *gta02_devices_pmu_children[] = { &s3c_device_ts, /* input 1 */ >a02_pm_gsm_dev, >a02_pm_usbhost_dev, - &s3c_device_spi_acc1, /* input 2 */ - &s3c_device_spi_acc2, /* input 3 */ + >a02_spi_gpio_dev, /* input 2 and 3 */ >a02_button_dev, /* input 4 */ >a02_resume_reason_device, }; diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c index 3def0c41f8b..772c09c4a49 100644 --- a/drivers/input/misc/lis302dl.c +++ b/drivers/input/misc/lis302dl.c @@ -40,18 +40,51 @@ #include #include #include +#include #include /* Utility functions */ static u8 __reg_read(struct lis302dl_info *lis, u8 reg) { - return (lis->pdata->lis302dl_bitbang_reg_read)(lis, reg); + struct spi_message msg; + struct spi_transfer t; + u8 data[2] = {0xc0 | reg}; + int rc; + + spi_message_init(&msg); + memset(&t, 0, sizeof t); + t.len = 2; + spi_message_add_tail(&t, &msg); + t.tx_buf = &data[0]; + t.rx_buf = &data[0]; + + /* Should complete without blocking */ + rc = spi_non_blocking_transfer(lis->spi, &msg); + if (rc < 0) { + dev_err(lis->dev, "Error reading register\n"); + return rc; + } + + return data[1]; } static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val) -{ - (lis->pdata->lis302dl_bitbang_reg_write)(lis, reg, val); +{ + struct spi_message msg; + struct spi_transfer t; + u8 data[2] = {reg, val}; + + spi_message_init(&msg); + memset(&t, 0, sizeof t); + t.len = 2; + spi_message_add_tail(&t, &msg); + t.tx_buf = &data[0]; + t.rx_buf = &data[0]; + + /* Completes without blocking */ + if (spi_non_blocking_transfer(lis->spi, &msg) < 0) + dev_err(lis->dev, "Error writing register\n"); } static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask, @@ -213,15 +246,28 @@ static void _report_btn_double(struct input_dev *inp, int btn) static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis) { - u8 data = 0xc0 | LIS302DL_REG_STATUS; /* read, autoincrement */ - u8 read[(LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS) + 1]; + u8 data[(LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS) + 2] = {0xC0 | LIS302DL_REG_STATUS}; + u8 *read = data + 1; unsigned long flags; int mg_per_sample = __threshold_to_mg(lis, 1); + struct spi_message msg; + struct spi_transfer t; + + spi_message_init(&msg); + memset(&t, 0, sizeof t); + t.len = sizeof(data); + spi_message_add_tail(&t, &msg); + t.tx_buf = &data[0]; + t.rx_buf = &data[0]; /* grab the set of register containing status and XYZ data */ local_irq_save(flags); - (lis->pdata->lis302dl_bitbang)(lis, &data, 1, &read[0], sizeof(read)); + + /* Should complete without blocking */ + if (spi_non_blocking_transfer(lis->spi, &msg) < 0) + dev_err(lis->dev, "Error reading registers\n"); + local_irq_restore(flags); /* @@ -601,19 +647,27 @@ static int __lis302dl_reset_device(struct lis302dl_info *lis) return !!(timeout < 0); } -static int __devinit lis302dl_probe(struct platform_device *pdev) +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 = pdev->dev.platform_data; + struct lis302dl_platform_data *pdata = spi->dev.platform_data; + + spi->mode = SPI_MODE_3; + rc = spi_setup(spi); + if (rc < 0) { + dev_err(&spi->dev, "spi_setup failed\n"); + return rc; + } lis = kzalloc(sizeof(*lis), GFP_KERNEL); if (!lis) return -ENOMEM; - lis->dev = &pdev->dev; + lis->dev = &spi->dev; + lis->spi = spi; dev_set_drvdata(lis->dev, lis); @@ -736,9 +790,9 @@ bail_free_lis: return rc; } -static int __devexit lis302dl_remove(struct platform_device *pdev) +static int __devexit lis302dl_remove(struct spi_device *spi) { - struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); unsigned long flags; /* Disable interrupts */ @@ -754,7 +808,7 @@ static int __devexit lis302dl_remove(struct platform_device *pdev) local_irq_restore(flags); /* Cleanup resources */ - sysfs_remove_group(&pdev->dev.kobj, &lis302dl_attr_group); + 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); @@ -785,9 +839,9 @@ static u8 regs_to_save[] = { }; -static int lis302dl_suspend(struct platform_device *pdev, pm_message_t state) +static int lis302dl_suspend(struct spi_device *spi, pm_message_t state) { - struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); unsigned long flags; u_int8_t tmp; int n; @@ -830,9 +884,9 @@ static int lis302dl_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int lis302dl_resume(struct platform_device *pdev) +static int lis302dl_resume(struct spi_device *spi) { - struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); + struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); unsigned long flags; int n; @@ -853,7 +907,7 @@ static int lis302dl_resume(struct platform_device *pdev) mdelay(1); if (__lis302dl_reset_device(lis)) - dev_err(&pdev->dev, "device BOOT reload failed\n"); + dev_err(&spi->dev, "device BOOT reload failed\n"); lis->regs[LIS302DL_REG_CTRL1] |= LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen | @@ -878,26 +932,26 @@ static int lis302dl_resume(struct platform_device *pdev) #define lis302dl_resume NULL #endif -static struct platform_driver lis302dl_driver = { +static struct spi_driver lis302dl_spi_driver = { .driver = { - .name = "lis302dl", - .owner = THIS_MODULE, + .name = "lis302dl", + .owner = THIS_MODULE, }, - .probe = lis302dl_probe, - .remove = __devexit_p(lis302dl_remove), + .probe = lis302dl_probe, + .remove = __devexit_p(lis302dl_remove), .suspend = lis302dl_suspend, .resume = lis302dl_resume, }; static int __devinit lis302dl_init(void) { - return platform_driver_register(&lis302dl_driver); + return spi_register_driver(&lis302dl_spi_driver); } static void __exit lis302dl_exit(void) { - platform_driver_unregister(&lis302dl_driver); + spi_unregister_driver(&lis302dl_spi_driver); } MODULE_AUTHOR("Harald Welte "); diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h index 01c4ac97a5b..0c1fc309731 100644 --- a/include/linux/lis302dl.h +++ b/include/linux/lis302dl.h @@ -4,7 +4,7 @@ #include #include #include - +#include struct lis302dl_info; @@ -16,12 +16,7 @@ struct lis302dl_platform_data { unsigned long pin_miso; int open_drain; int interrupt; - void (*lis302dl_bitbang)(struct lis302dl_info *lis, u8 *tx, - int tx_bytes, u8 *rx, int rx_bytes); void (*lis302dl_suspend_io)(struct lis302dl_info *, int resuming); - int (*lis302dl_bitbang_reg_read)(struct lis302dl_info *, u8 reg); - void (*lis302dl_bitbang_reg_write)(struct lis302dl_info *, u8 reg, - u8 val); }; struct lis302dl_info { @@ -36,6 +31,8 @@ struct lis302dl_info { unsigned int threshold; /* mg */ unsigned int duration; /* ms */ } wakeup; + + struct spi_device *spi; u_int8_t regs[0x40]; }; -- cgit v1.2.3