aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-s3c2442/mach-gta02.c214
-rw-r--r--drivers/input/misc/lis302dl.c102
-rw-r--r--include/linux/lis302dl.h9
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 <mach/spi-gpio.h>
#include <mach/usb-control.h>
#include <mach/regs-mem.h>
+#include <mach/spi-gpio.h>
#include <plat/pwm.h>
#include <mach/gta02.h>
@@ -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 = &gta02_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 */
&gta02_pm_gsm_dev,
&gta02_pm_usbhost_dev,
- &s3c_device_spi_acc1, /* input 2 */
- &s3c_device_spi_acc2, /* input 3 */
+ &gta02_spi_gpio_dev, /* input 2 and 3 */
&gta02_button_dev, /* input 4 */
&gta02_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 <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
#include <linux/lis302dl.h>
/* 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 <laforge@openmoko.org>");
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 <linux/types.h>
#include <linux/spi/spi.h>
#include <linux/input.h>
-
+#include <linux/workqueue.h>
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];
};