diff options
Diffstat (limited to 'recipes/linux/linux-2.6.39.2/shr.patch')
-rw-r--r-- | recipes/linux/linux-2.6.39.2/shr.patch | 4130 |
1 files changed, 4130 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.39.2/shr.patch b/recipes/linux/linux-2.6.39.2/shr.patch new file mode 100644 index 0000000000..7ff30cd593 --- /dev/null +++ b/recipes/linux/linux-2.6.39.2/shr.patch @@ -0,0 +1,4130 @@ +All patches from shr kernel repository +rebased on top of openmoko kernel repository + +https://gitorious.org/shr/linux/commits/shr-2.6.39-nodrm + +c2046f23 lis302dl: use ABS events rather then REL events +d51b2df input: lis302dl: fix the resume path +9157619 lis302dl accelerometer driver +0f0f23a Force GPS power up on resume if it were powered up on suspend +1c83000 Fix high power consumption in suspend +8dae2c6 wm8753: use snd_soc_jack on neo1973 +104353d ar6000_delay.patch +235c131 usbhost.patch +e342a4d Enable powering off after 8s POWER press +b9dfb66 glamo-display: fix WSOD for 242 timming +d952c22 Openmoko resume reason sysfs node ported from 2.6.29 +2d7ec7a nand/s3c2410: add mising badblocksbits value +2a067d5 glamo-mci: revert changes for Per's patchset +d9b3bc8 Revert "mmc: add none blocking mmc request function" +a194e92 Revert "mmc: mmc_test: add debugfs file to list all tests" +8a3a7aa Revert "mmc: mmc_test: add test for none blocking transfers" +711299a Revert "mmc: add member in mmc queue struct to hold request data" +101511c Revert "mmc: add a block request prepare function" +023e4b8 Revert "mmc: move error code in mmc_block_issue_rw_rq to a separate function." +6dd67d8 Revert "mmc: add a second mmc queue request member" +cc53cf6 Revert "mmc: add handling for two parallel block requests in issue_rw_rq" +76cc5df Revert "mmc: test: add random fault injection in core.c" + +diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile +index 035d116..16d9855 100644 +--- a/arch/arm/mach-s3c2440/Makefile ++++ b/arch/arm/mach-s3c2440/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \ + gta02-pm-bt.o \ + gta02-pm-gps.o \ + gta02-pm-gsm.o \ ++ gta02-pm-usbhost.o \ + gta02-pm-wlan.o \ + gta02-fiq.o \ + gta02-hdq.o \ +diff --git a/arch/arm/mach-s3c2440/gta02-pm-gps.c b/arch/arm/mach-s3c2440/gta02-pm-gps.c +index 4ca3ac6..7501978 100644 +--- a/arch/arm/mach-s3c2440/gta02-pm-gps.c ++++ b/arch/arm/mach-s3c2440/gta02-pm-gps.c +@@ -42,7 +42,7 @@ int gta02_pm_gps_is_on(void) + EXPORT_SYMBOL_GPL(gta02_pm_gps_is_on); + + /* This is the POWERON pin */ +-static void gps_pwron_set(int on) ++static void gps_pwron_set(int on, int ignore_state) + { + if (on) { + /* return UART pins to being UART pins */ +@@ -50,7 +50,7 @@ static void gps_pwron_set(int on) + /* remove pulldown now it won't be floating any more */ + s3c_gpio_setpull(S3C2410_GPH(5), S3C_GPIO_PULL_NONE); + +- if (!gta02_gps.power_was_on) ++ if (!gta02_gps.power_was_on || ignore_state) + regulator_enable(gta02_gps.regulator); + } else { + /* +@@ -61,7 +61,7 @@ static void gps_pwron_set(int on) + gpio_set_value(S3C2410_GPH(4), 0); + /* don't let RX from unpowered GPS float */ + s3c_gpio_setpull(S3C2410_GPH(5), S3C_GPIO_PULL_DOWN); +- if (gta02_gps.power_was_on) ++ if (gta02_gps.power_was_on || ignore_state) + regulator_disable(gta02_gps.regulator); + } + } +@@ -113,7 +113,7 @@ static ssize_t power_gps_write(struct device *dev, + return ret; + + if (!strcmp(attr->attr.name, "power_on")) { +- gps_pwron_set(on); ++ gps_pwron_set(on, 0); + gta02_gps.power_was_on = !!on; + #ifdef CONFIG_PM + } else if (!strcmp(attr->attr.name, "keep_on_in_suspend")) { +@@ -128,7 +128,7 @@ static int gta02_pm_gps_suspend(struct device *dev) + { + if (!gta02_gps.keep_on_in_suspend || + !gta02_gps.power_was_on) +- gps_pwron_set(0); ++ gps_pwron_set(0, 0); + else + dev_warn(dev, "GTA02: keeping gps ON " + "during suspend\n"); +@@ -138,7 +138,7 @@ static int gta02_pm_gps_suspend(struct device *dev) + static int gta02_pm_gps_resume(struct device *dev) + { + if (!gta02_gps.keep_on_in_suspend && gta02_gps.power_was_on) +- gps_pwron_set(1); ++ gps_pwron_set(1, 1); + + return 0; + } +diff --git a/arch/arm/mach-s3c2440/gta02-pm-usbhost.c b/arch/arm/mach-s3c2440/gta02-pm-usbhost.c +new file mode 100644 +index 0000000..233340a +--- /dev/null ++++ b/arch/arm/mach-s3c2440/gta02-pm-usbhost.c +@@ -0,0 +1,174 @@ ++/* ++ * USBHOST Management code for the Openmoko Freerunner GSM Phone ++ * ++ * (C) 2007 by Openmoko Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * All rights reserved. ++ * ++ * 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/console.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/regulator/consumer.h> ++ ++#include <mach/gpio.h> ++#include <asm/mach-types.h> ++ ++#include <mach/hardware.h> ++ ++#include <mach/gta02.h> ++#include <mach/regs-gpio.h> ++#include <mach/regs-gpioj.h> ++ ++static struct regulator *gta02_usbhost_regulator; ++ ++static ssize_t usbhost_read(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ if (!strcmp(attr->attr.name, "power_on")) { ++ if (regulator_is_enabled(gta02_usbhost_regulator)) ++ goto out_1; ++ } ++ ++ return strlcpy(buf, "0\n", 3); ++out_1: ++ return strlcpy(buf, "1\n", 3); ++} ++ ++static void usbhost_on_off(struct device *dev, int on) ++{ ++ ++ on = !!on; ++ ++ if (on == regulator_is_enabled(gta02_usbhost_regulator)) ++ return; ++ ++ if (!on) { ++ regulator_disable(gta02_usbhost_regulator); ++ return; ++ } ++ ++ regulator_enable(gta02_usbhost_regulator); ++} ++ ++static ssize_t usbhost_write(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long on = simple_strtoul(buf, NULL, 10); ++ ++ if (!strcmp(attr->attr.name, "power_on")) { ++ usbhost_on_off(dev, on); ++ ++ return count; ++ } ++ ++ return count; ++} ++ ++static DEVICE_ATTR(power_on, 0644, usbhost_read, usbhost_write); ++ ++#ifdef CONFIG_PM ++ ++static int gta02_usbhost_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int gta02_usbhost_suspend_late(struct device *dev) ++{ ++ return 0; ++} ++ ++static int gta02_usbhost_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static struct dev_pm_ops gta02_usbhost_pm_ops = { ++ .suspend = gta02_usbhost_suspend, ++ .suspend_noirq = gta02_usbhost_suspend_late, ++ .resume = gta02_usbhost_resume, ++}; ++ ++#define GTA02_USBHOST_PM_OPS (>a02_usbhost_pm_ops) ++ ++#else ++#define GTA02_USBHOST_PM_OPS NULL ++#endif /* CONFIG_PM */ ++ ++static struct attribute *gta02_usbhost_sysfs_entries[] = { ++ &dev_attr_power_on.attr, ++ NULL ++}; ++ ++static struct attribute_group gta02_usbhost_attr_group = { ++ .name = NULL, ++ .attrs = gta02_usbhost_sysfs_entries, ++}; ++ ++static int __init gta02_usbhost_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ gta02_usbhost_regulator = regulator_get_exclusive(&pdev->dev, "USBHOST"); ++ ++ if (IS_ERR(gta02_usbhost_regulator)) { ++ ret = PTR_ERR(gta02_usbhost_regulator); ++ dev_err(&pdev->dev, "Failed to get regulator: %d\n", ret); ++ return ret; ++ } ++ ++ ret = sysfs_create_group(&pdev->dev.kobj, >a02_usbhost_attr_group); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to create sysfs entries: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gta02_usbhost_remove(struct platform_device *pdev) ++{ ++ usbhost_on_off(&pdev->dev, 0); ++ ++ sysfs_remove_group(&pdev->dev.kobj, >a02_usbhost_attr_group); ++ regulator_put(gta02_usbhost_regulator); ++ ++ return 0; ++} ++ ++static struct platform_driver gta02_usbhost_driver = { ++ .probe = gta02_usbhost_probe, ++ .remove = gta02_usbhost_remove, ++ .driver = { ++ .name = "gta02-pm-usbhost", ++ .pm = GTA02_USBHOST_PM_OPS, ++ }, ++}; ++ ++static int __devinit gta02_usbhost_init(void) ++{ ++ return platform_driver_register(>a02_usbhost_driver); ++} ++module_init(gta02_usbhost_init); ++ ++static void gta02_usbhost_exit(void) ++{ ++ platform_driver_unregister(>a02_usbhost_driver); ++} ++module_exit(gta02_usbhost_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); ++MODULE_DESCRIPTION("Openmoko Freerunner USBHOST Power Management"); +diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c +index aa31b8d..6f82f5a 100644 +--- a/arch/arm/mach-s3c2440/mach-gta02.c ++++ b/arch/arm/mach-s3c2440/mach-gta02.c +@@ -62,6 +62,7 @@ + + #include <linux/input.h> + #include <linux/gpio_keys.h> ++#include <linux/lis302dl.h> + + #include <linux/leds.h> + #include <linux/leds_pwm.h> +@@ -127,6 +128,10 @@ static long gta02_panic_blink(int state) + return delay; + } + ++struct platform_device gta02_resume_reason_device = { ++ .name = "neo1973-resume", ++ .num_resources = 0, ++}; + + static struct map_desc gta02_iodesc[] __initdata = { + { +@@ -177,6 +182,10 @@ static struct platform_device gta02_pm_gsm_dev = { + .name = "gta02-pm-gsm", + }; + ++static struct platform_device gta02_pm_usbhost_dev = { ++ .name = "gta02-pm-usbhost", ++}; ++ + static struct platform_device gta02_pm_wlan_dev = { + .name = "gta02-pm-wlan", + }; +@@ -186,6 +195,11 @@ static struct regulator_consumer_supply gsm_supply_consumer = { + .supply = "GSM", + }; + ++static struct regulator_consumer_supply usbhost_supply_consumer = { ++ .dev = >a02_pm_usbhost_dev.dev, ++ .supply = "USBHOST", ++}; ++ + static struct regulator_init_data gsm_supply_init_data = { + .constraints = { + .min_uV = 3700000, +@@ -197,6 +211,17 @@ static struct regulator_init_data gsm_supply_init_data = { + .consumer_supplies = &gsm_supply_consumer, + }; + ++static struct regulator_init_data usbhost_supply_init_data = { ++ .constraints = { ++ .min_uV = 3700000, ++ .max_uV = 3700000, ++ .valid_modes_mask = REGULATOR_MODE_NORMAL, ++ .valid_ops_mask = REGULATOR_CHANGE_STATUS, ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &usbhost_supply_consumer, ++}; ++ + static struct fixed_voltage_config gsm_supply_config = { + .supply_name = "GSM", + .microvolts = 3700000, +@@ -205,6 +230,14 @@ static struct fixed_voltage_config gsm_supply_config = { + .init_data = &gsm_supply_init_data, + }; + ++static struct fixed_voltage_config usbhost_supply_config = { ++ .supply_name = "USBHOST", ++ .microvolts = 3700000, ++ .gpio = GTA02_GPIO_PCF(PCF50633_GPO), ++ .enable_high = 1, ++ .init_data = &usbhost_supply_init_data, ++}; ++ + static struct platform_device gta02_gsm_supply_device = { + .name = "reg-fixed-voltage", + .id = 1, +@@ -213,6 +246,14 @@ static struct platform_device gta02_gsm_supply_device = { + }, + }; + ++static struct platform_device gta02_usbhost_supply_device = { ++ .name = "reg-fixed-voltage", ++ .id = 2, ++ .dev = { ++ .platform_data = &usbhost_supply_config, ++ }, ++}; ++ + /* + * we crank down SD Card clock dynamically when GPS is powered + */ +@@ -297,6 +338,202 @@ static struct glamo_platform_data gta02_glamo_pdata = { + .glamo_external_reset = gta02_glamo_external_reset, + }; + ++/* 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); ++ ++} ++ ++ ++void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume) ++{ ++ struct lis302dl_platform_data *pdata = lis->pdata; ++ ++ if (!resume) { ++ /* ++ * we don't want to power them with a high level ++ * because GSENSOR_3V3 is not up during suspend ++ */ ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 0); ++ s3c2410_gpio_setpin(pdata->pin_clk, 0); ++ s3c2410_gpio_setpin(pdata->pin_mosi, 0); ++ /* misnomer: it is a pullDOWN in 2442 */ ++ s3c2410_gpio_pullup(pdata->pin_miso, 1); ++ return; ++ } ++ ++ /* back to normal */ ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 1); ++ s3c2410_gpio_setpin(pdata->pin_clk, 1); ++ /* misnomer: it is a pullDOWN in 2442 */ ++ s3c2410_gpio_pullup(pdata->pin_miso, 0); ++ ++ s3c2410_gpio_cfgpin(pdata->pin_chip_select, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_clk, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_mosi, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_miso, S3C2410_GPIO_INPUT); ++ ++} ++ ++ ++ ++struct lis302dl_platform_data lis302_pdata_top = { ++ .name = "lis302-1 (top)", ++ .pin_chip_select= S3C2410_GPD(12), ++ .pin_clk = S3C2410_GPG(7), ++ .pin_mosi = S3C2410_GPG(6), ++ .pin_miso = S3C2410_GPG(5), ++ .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, ++}; ++ ++struct lis302dl_platform_data lis302_pdata_bottom = { ++ .name = "lis302-2 (bottom)", ++ .pin_chip_select= S3C2410_GPD(13), ++ .pin_clk = S3C2410_GPG(7), ++ .pin_mosi = S3C2410_GPG(6), ++ .pin_miso = S3C2410_GPG(5), ++ .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 platform_device s3c_device_spi_acc1 = { ++ .name = "lis302dl", ++ .id = 1, ++ .dev = { ++ .platform_data = &lis302_pdata_top, ++ }, ++}; ++ ++static struct platform_device s3c_device_spi_acc2 = { ++ .name = "lis302dl", ++ .id = 2, ++ .dev = { ++ .platform_data = &lis302_pdata_bottom, ++ }, ++}; ++ + /* JBT6k74 display controller */ + static void gta02_jbt6k74_probe_completed(struct device *dev) + { +@@ -487,6 +724,11 @@ static struct regulator_consumer_supply hcldo_consumers[] = { + }, + }; + ++static void gta02_poweroff(void) ++{ ++ pcf50633_reg_set_bit_mask(gta02_pcf, PCF50633_REG_OOCSHDWN, 1, 1); ++} ++ + struct pcf50633_platform_data gta02_pcf_pdata = { + .resumers = { + [0] = PCF50633_INT1_USBINS | +@@ -522,7 +764,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { + .min_uV = 1300000, + .max_uV = 1600000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, +- .always_on = 1, ++ .always_on = 0, + .apply_uV = 1, + }, + }, +@@ -614,6 +856,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { + }, + .probe_done = gta02_pmu_attach_child_devices, + .mbc_event_callback = gta02_pmu_event_callback, ++ .force_shutdown = gta02_poweroff, + }; + + +@@ -1053,6 +1296,9 @@ static struct platform_device *gta02_devices[] __initdata = { + static struct platform_device *gta02_devices_pmu_children[] = { + >a02_hdq_device, + >a02_platform_bat, ++ >a02_resume_reason_device, ++ &s3c_device_spi_acc1, ++ &s3c_device_spi_acc2, + }; + + +@@ -1080,11 +1326,6 @@ static void gta02_pmu_attach_child_devices(struct pcf50633 *pcf) + ARRAY_SIZE(gta02_devices_pmu_children)); + } + +-static void gta02_poweroff(void) +-{ +- pcf50633_reg_set_bit_mask(gta02_pcf, PCF50633_REG_OOCSHDWN, 1, 1); +-} +- + struct gta02_device_children { + const char *dev_name; + size_t num_children; +@@ -1098,12 +1339,17 @@ static struct platform_device *gta02_glamo_gpio_children[] = { + + static struct platform_device *gta02_pcf50633_gpio_children[] = { + >a02_gsm_supply_device, ++ >a02_usbhost_supply_device, + }; + + static struct platform_device *gta02_gsm_supply_children[] = { + >a02_pm_gsm_dev, + }; + ++static struct platform_device* gta02_usbhost_supply_children[] = { ++ >a02_pm_usbhost_dev, ++}; ++ + static struct platform_device *gta02_hdq_children[] = { + &bq27000_battery_device, + }; +@@ -1126,6 +1372,11 @@ static struct gta02_device_children gta02_device_children[] = { + .children = gta02_gsm_supply_children, + }, + { ++ .dev_name = "reg-fixed-voltage.2", ++ .num_children = 1, ++ .children = gta02_usbhost_supply_children, ++ }, ++ { + .dev_name = "spi2.0", + .probed_callback = gta02_jbt6k74_probe_completed, + }, +@@ -1199,6 +1450,10 @@ static void __init gta02_machine_init(void) + + s3c_pm_init(); + ++ /* we need push-pull interrupt from motion sensors */ ++ lis302_pdata_top.open_drain = 0; ++ lis302_pdata_bottom.open_drain = 0; ++ + #ifdef CONFIG_CHARGER_PCF50633 + INIT_DELAYED_WORK(>a02_charger_work, gta02_charger_worker); + #endif +diff --git a/drivers/ar6000/hif/hif2.c b/drivers/ar6000/hif/hif2.c +index 386d96e..90178d0 100644 +--- a/drivers/ar6000/hif/hif2.c ++++ b/drivers/ar6000/hif/hif2.c +@@ -517,6 +517,8 @@ static int ar6000_do_activate(struct hif_device *hif) + goto out_func_ready; + } + ++ mdelay (10); ++ + ret = htcCallbacks.deviceInsertedHandler(hif); + if (ret == A_OK) + return 0; +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index f9cf088..fb6c6aa 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -467,4 +467,13 @@ config INPUT_XEN_KBDDEV_FRONTEND + To compile this driver as a module, choose M here: the + module will be called xen-kbdfront. + ++config INPUT_LIS302DL ++ tristate "STmicro LIS302DL 3-axis accelerometer" ++ depends on SPI_MASTER ++ help ++ SPI driver for the STmicro LIS302DL 3-axis accelerometer. ++ ++ The userspece interface is a 3-axis (X/Y/Z) relative movement ++ Linux input device, reporting REL_[XYZ] events. ++ + endif +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index e3f7984..e341378 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -44,4 +44,5 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o + obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o + obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o + obj-$(CONFIG_INPUT_YEALINK) += yealink.o ++obj-$(CONFIG_INPUT_LIS302DL) += lis302dl.o + +diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c +new file mode 100644 +index 0000000..1ba7a8f +--- /dev/null ++++ b/drivers/input/misc/lis302dl.c +@@ -0,0 +1,898 @@ ++/* Linux kernel driver for the ST LIS302D 3-axis accelerometer ++ * ++ * Copyright (C) 2007-2008 by Openmoko, Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * converted to private bitbang by: ++ * Andy Green <andy@openmoko.com> ++ * ability to set acceleration threshold added by: ++ * Simon Kagstrom <simon.kagstrom@gmail.com> ++ * All rights reserved. ++ * ++ * 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 ++ * ++ * TODO ++ * * statistics for overflow events ++ * * configuration interface (sysfs) for ++ * * enable/disable x/y/z axis data ready ++ * * enable/disable resume from freee fall / click ++ * * free fall / click parameters ++ * * high pass filter parameters ++ */ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/sysfs.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); ++} ++ ++static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val) ++{ ++ (lis->pdata->lis302dl_bitbang_reg_write)(lis, reg, val); ++} ++ ++static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask, ++ u8 val) ++{ ++ u_int8_t tmp; ++ ++ val &= mask; ++ ++ tmp = __reg_read(lis, reg); ++ tmp &= ~mask; ++ tmp |= val; ++ __reg_write(lis, reg, tmp); ++} ++ ++static int __ms_to_duration(struct lis302dl_info *lis, int ms) ++{ ++ /* If we have 400 ms sampling rate, the stepping is 2.5 ms, ++ * on 100 ms the stepping is 10ms */ ++ if (lis->flags & LIS302DL_F_DR) ++ return min((ms * 10) / 25, 637); ++ ++ return min(ms / 10, 2550); ++} ++ ++static int __duration_to_ms(struct lis302dl_info *lis, int duration) ++{ ++ if (lis->flags & LIS302DL_F_DR) ++ return (duration * 25) / 10; ++ ++ return duration * 10; ++} ++ ++static u8 __mg_to_threshold(struct lis302dl_info *lis, int mg) ++{ ++ /* If FS is set each bit is 71mg, otherwise 18mg. The THS register ++ * has 7 bits for the threshold value */ ++ if (lis->flags & LIS302DL_F_FS) ++ return min(mg / 71, 127); ++ ++ return min(mg / 18, 127); ++} ++ ++static int __threshold_to_mg(struct lis302dl_info *lis, u8 threshold) ++{ ++ if (lis->flags & LIS302DL_F_FS) ++ return threshold * 71; ++ ++ return threshold * 18; ++} ++ ++/* interrupt handling related */ ++ ++enum lis302dl_intmode { ++ LIS302DL_INTMODE_GND = 0x00, ++ LIS302DL_INTMODE_FF_WU_1 = 0x01, ++ 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 device *dev, int int_pin, ++ enum lis302dl_intmode mode) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ switch (int_pin) { ++ case 1: ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode); ++ break; ++ case 2: ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void __enable_wakeup(struct lis302dl_info *lis) ++{ ++ __reg_write(lis, LIS302DL_REG_CTRL1, 0); ++ ++ /* First zero to get to a known state */ ++ __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, LIS302DL_FFWUCFG_XHIE | ++ LIS302DL_FFWUCFG_YHIE | LIS302DL_FFWUCFG_ZHIE | ++ LIS302DL_FFWUCFG_LIR); ++ __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, ++ __mg_to_threshold(lis, lis->wakeup.threshold)); ++ __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, ++ __ms_to_duration(lis, lis->wakeup.duration)); ++ ++ /* Route the interrupt for wakeup */ ++ __lis302dl_int_mode(lis->dev, 1, ++ LIS302DL_INTMODE_FF_WU_1); ++ ++ __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET); ++ __reg_read(lis, LIS302DL_REG_OUT_X); ++ __reg_read(lis, LIS302DL_REG_OUT_Y); ++ __reg_read(lis, LIS302DL_REG_OUT_Z); ++ __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_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | 7); ++} ++ ++static void __enable_data_collection(struct lis302dl_info *lis) ++{ ++ u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen | ++ LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen; ++ ++ /* make sure we're powered up and generate data ready */ ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1); ++ ++ /* If the threshold is zero, let the device generated an interrupt ++ * on each datum */ ++ if (lis->threshold == 0) { ++ __reg_write(lis, LIS302DL_REG_CTRL2, 0); ++ __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_DATA_READY); ++ __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_DATA_READY); ++ } else { ++ __reg_write(lis, LIS302DL_REG_CTRL2, ++ LIS302DL_CTRL2_HPFF1); ++ __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, ++ __mg_to_threshold(lis, lis->threshold)); ++ __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, ++ __ms_to_duration(lis, lis->duration)); ++ ++ /* Clear the HP filter "starting point" */ ++ __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET); ++ __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, ++ LIS302DL_FFWUCFG_XHIE | LIS302DL_FFWUCFG_YHIE | ++ LIS302DL_FFWUCFG_ZHIE | LIS302DL_FFWUCFG_LIR); ++ __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_FF_WU_12); ++ __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_FF_WU_12); ++ } ++} ++ ++#if 0 ++static void _report_btn_single(struct input_dev *inp, int btn) ++{ ++ input_report_key(inp, btn, 1); ++ input_sync(inp); ++ input_report_key(inp, btn, 0); ++} ++ ++static void _report_btn_double(struct input_dev *inp, int btn) ++{ ++ input_report_key(inp, btn, 1); ++ input_sync(inp); ++ input_report_key(inp, btn, 0); ++ input_sync(inp); ++ input_report_key(inp, btn, 1); ++ input_sync(inp); ++ input_report_key(inp, btn, 0); ++} ++#endif ++ ++ ++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]; ++ unsigned long flags; ++ int mg_per_sample = __threshold_to_mg(lis, 1); ++ ++ /* 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)); ++ local_irq_restore(flags); ++ ++ /* ++ * at the minute the test below fails 50% of the time due to ++ * a problem with level interrupts causing ISRs to get called twice. ++ * This is a workaround for that, but actually this test is still ++ * valid and the information can be used for overrrun stats. ++ */ ++ ++ /* has any kind of overrun been observed by the lis302dl? */ ++ if (read[0] & (LIS302DL_STATUS_XOR | ++ LIS302DL_STATUS_YOR | ++ LIS302DL_STATUS_ZOR)) ++ lis->overruns++; ++ ++ /* we have a valid sample set? */ ++ if (read[0] & LIS302DL_STATUS_XYZDA) { ++ input_report_abs(lis->input_dev, ABS_X, mg_per_sample * ++ (s8)read[LIS302DL_REG_OUT_X - LIS302DL_REG_STATUS]); ++ input_report_abs(lis->input_dev, ABS_Y, mg_per_sample * ++ (s8)read[LIS302DL_REG_OUT_Y - LIS302DL_REG_STATUS]); ++ input_report_abs(lis->input_dev, ABS_Z, mg_per_sample * ++ (s8)read[LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS]); ++ ++ input_sync(lis->input_dev); ++ } ++ ++ if (lis->threshold) ++ /* acknowledge the wakeup source */ ++ __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1); ++} ++ ++static irqreturn_t lis302dl_interrupt(int irq, void *_lis) ++{ ++ struct lis302dl_info *lis = _lis; ++ ++ lis302dl_bitbang_read_sample(lis); ++ return IRQ_HANDLED; ++} ++ ++/* sysfs */ ++ ++static ssize_t show_overruns(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", lis->overruns); ++} ++ ++static DEVICE_ATTR(overruns, S_IRUGO, show_overruns, NULL); ++ ++static ssize_t show_rate(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ u8 ctrl1; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1); ++ local_irq_restore(flags); ++ ++ return sprintf(buf, "%d\n", ctrl1 & LIS302DL_CTRL1_DR ? 400 : 100); ++} ++ ++static ssize_t set_rate(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ if (!strcmp(buf, "400\n")) { ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, ++ LIS302DL_CTRL1_DR); ++ lis->flags |= LIS302DL_F_DR; ++ } else { ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, ++ 0); ++ lis->flags &= ~LIS302DL_F_DR; ++ } ++ local_irq_restore(flags); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(sample_rate, S_IRUGO | S_IWUSR, show_rate, set_rate); ++ ++static ssize_t show_scale(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ u_int8_t ctrl1; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1); ++ local_irq_restore(flags); ++ ++ return sprintf(buf, "%s\n", ctrl1 & LIS302DL_CTRL1_FS ? "9.2" : "2.3"); ++} ++ ++static ssize_t set_scale(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ if (!strcmp(buf, "9.2\n")) { ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, ++ LIS302DL_CTRL1_FS); ++ lis->flags |= LIS302DL_F_FS; ++ } else { ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, ++ 0); ++ lis->flags &= ~LIS302DL_F_FS; ++ } ++ ++ if (lis->flags & LIS302DL_F_INPUT_OPEN) ++ __enable_data_collection(lis); ++ ++ local_irq_restore(flags); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale); ++ ++static ssize_t show_threshold(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ /* Display the device view of the threshold setting */ ++ return sprintf(buf, "%d\n", __threshold_to_mg(lis, ++ __mg_to_threshold(lis, lis->threshold))); ++} ++ ++static ssize_t set_threshold(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned int val; ++ ++ if (sscanf(buf, "%u\n", &val) != 1) ++ return -EINVAL; ++ /* 8g is the maximum if FS is 1 */ ++ if (val > 8000) ++ return -ERANGE; ++ ++ /* Set the threshold and write it out if the device is used */ ++ lis->threshold = val; ++ ++ if (lis->flags & LIS302DL_F_INPUT_OPEN) { ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ __enable_data_collection(lis); ++ local_irq_restore(flags); ++ } ++ ++ return count; ++} ++ ++static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, show_threshold, set_threshold); ++ ++static ssize_t show_duration(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", __duration_to_ms(lis, ++ __ms_to_duration(lis, lis->duration))); ++} ++ ++static ssize_t set_duration(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned int val; ++ ++ if (sscanf(buf, "%u\n", &val) != 1) ++ return -EINVAL; ++ if (val > 2550) ++ return -ERANGE; ++ ++ lis->duration = val; ++ if (lis->flags & LIS302DL_F_INPUT_OPEN) ++ __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, ++ __ms_to_duration(lis, lis->duration)); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(duration, S_IRUGO | S_IWUSR, show_duration, set_duration); ++ ++static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ int n = 0; ++ u8 reg[0x40]; ++ char *end = buf; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ for (n = 0; n < sizeof(reg); n++) ++ reg[n] = __reg_read(lis, n); ++ ++ local_irq_restore(flags); ++ ++ for (n = 0; n < sizeof(reg); n += 16) { ++ hex_dump_to_buffer(reg + n, 16, 16, 1, end, 128, 0); ++ end += strlen(end); ++ *end++ = '\n'; ++ *end++ = '\0'; ++ } ++ ++ return end - buf; ++} ++static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL); ++ ++/* Configure freefall/wakeup interrupts */ ++static ssize_t set_wakeup_threshold(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned int threshold; ++ ++ if (sscanf(buf, "%u\n", &threshold) != 1) ++ return -EINVAL; ++ ++ if (threshold > 8000) ++ return -ERANGE; ++ ++ /* Zero turns the feature off */ ++ if (threshold == 0) { ++ if (lis->flags & LIS302DL_F_IRQ_WAKE) { ++ disable_irq_wake(lis->pdata->interrupt); ++ lis->flags &= ~LIS302DL_F_IRQ_WAKE; ++ } ++ ++ return count; ++ } ++ ++ lis->wakeup.threshold = threshold; ++ ++ if (!(lis->flags & LIS302DL_F_IRQ_WAKE)) { ++ enable_irq_wake(lis->pdata->interrupt); ++ lis->flags |= LIS302DL_F_IRQ_WAKE; ++ } ++ ++ return count; ++} ++ ++static ssize_t show_wakeup_threshold(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ /* All events off? */ ++ if (lis->wakeup.threshold == 0) ++ return sprintf(buf, "off\n"); ++ ++ return sprintf(buf, "%u\n", lis->wakeup.threshold); ++} ++ ++static DEVICE_ATTR(wakeup_threshold, S_IRUGO | S_IWUSR, show_wakeup_threshold, ++ set_wakeup_threshold); ++ ++static ssize_t set_wakeup_duration(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ unsigned int duration; ++ ++ if (sscanf(buf, "%u\n", &duration) != 1) ++ return -EINVAL; ++ ++ if (duration > 2550) ++ return -ERANGE; ++ ++ lis->wakeup.duration = duration; ++ ++ return count; ++} ++ ++static ssize_t show_wakeup_duration(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", lis->wakeup.duration); ++} ++ ++static DEVICE_ATTR(wakeup_duration, S_IRUGO | S_IWUSR, show_wakeup_duration, ++ set_wakeup_duration); ++ ++static struct attribute *lis302dl_sysfs_entries[] = { ++ &dev_attr_sample_rate.attr, ++ &dev_attr_full_scale.attr, ++ &dev_attr_threshold.attr, ++ &dev_attr_duration.attr, ++ &dev_attr_dump.attr, ++ &dev_attr_wakeup_threshold.attr, ++ &dev_attr_wakeup_duration.attr, ++ &dev_attr_overruns.attr, ++ NULL ++}; ++ ++static struct attribute_group lis302dl_attr_group = { ++ .name = NULL, ++ .attrs = lis302dl_sysfs_entries, ++}; ++ ++/* input device handling and driver core interaction */ ++ ++static int lis302dl_input_open(struct input_dev *inp) ++{ ++ struct lis302dl_info *lis = input_get_drvdata(inp); ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ __enable_data_collection(lis); ++ lis->flags |= LIS302DL_F_INPUT_OPEN; ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static void lis302dl_input_close(struct input_dev *inp) ++{ ++ struct lis302dl_info *lis = input_get_drvdata(inp); ++ u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen | ++ LIS302DL_CTRL1_Zen; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* since the input core already serializes access and makes sure we ++ * 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); ++ lis->flags &= ~LIS302DL_F_INPUT_OPEN; ++ ++ /* however, don't power down the whole device if still needed */ ++ if (!(lis->flags & LIS302DL_F_WUP_FF || ++ lis->flags & LIS302DL_F_WUP_CLICK)) { ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD, ++ 0x00); ++ } ++ local_irq_restore(flags); ++} ++ ++/* get the device to reload its coefficients from EEPROM and wait for it ++ * to complete ++ */ ++ ++static int __lis302dl_reset_device(struct lis302dl_info *lis) ++{ ++ int timeout = 10; ++ ++ __reg_write(lis, LIS302DL_REG_CTRL2, ++ LIS302DL_CTRL2_BOOT | LIS302DL_CTRL2_FDS); ++ ++ while ((__reg_read(lis, LIS302DL_REG_CTRL2) ++ & LIS302DL_CTRL2_BOOT) && (timeout--)) ++ mdelay(1); ++ ++ return !!(timeout < 0); ++} ++ ++static int __devinit lis302dl_probe(struct platform_device *pdev) ++{ ++ int rc; ++ struct lis302dl_info *lis; ++ u_int8_t wai; ++ unsigned long flags; ++ struct lis302dl_platform_data *pdata = pdev->dev.platform_data; ++ ++ lis = kzalloc(sizeof(*lis), GFP_KERNEL); ++ if (!lis) ++ return -ENOMEM; ++ ++ lis->dev = &pdev->dev; ++ ++ dev_set_drvdata(lis->dev, lis); ++ ++ lis->pdata = pdata; ++ ++ rc = sysfs_create_group(&lis->dev->kobj, &lis302dl_attr_group); ++ if (rc) { ++ dev_err(lis->dev, "error creating sysfs group\n"); ++ goto bail_free_lis; ++ } ++ ++ /* initialize input layer details */ ++ lis->input_dev = input_allocate_device(); ++ if (!lis->input_dev) { ++ dev_err(lis->dev, "Unable to allocate input device\n"); ++ goto bail_sysfs; ++ } ++ ++ input_set_drvdata(lis->input_dev, lis); ++ 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; ++ ++ rc = input_register_device(lis->input_dev); ++ if (rc) { ++ dev_err(lis->dev, "error %d registering input device\n", rc); ++ goto bail_inp_dev; ++ } ++ ++ local_irq_save(flags); ++ /* Configure our IO */ ++ (lis->pdata->lis302dl_suspend_io)(lis, 1); ++ ++ wai = __reg_read(lis, LIS302DL_REG_WHO_AM_I); ++ if (wai != LIS302DL_WHO_AM_I_MAGIC) { ++ dev_err(lis->dev, "unknown who_am_i signature 0x%02x\n", wai); ++ dev_set_drvdata(lis->dev, NULL); ++ rc = -ENODEV; ++ local_irq_restore(flags); ++ goto bail_inp_reg; ++ } ++ ++ set_bit(EV_ABS, lis->input_dev->evbit); ++ input_set_abs_params(lis->input_dev, ABS_X, 0, 0, 0, 0); ++ input_set_abs_params(lis->input_dev, ABS_Y, 0, 0, 0, 0); ++ input_set_abs_params(lis->input_dev, ABS_Z, 0, 0, 0, 0); ++ ++ ++ lis->threshold = 0; ++ lis->duration = 0; ++ memset(&lis->wakeup, 0, sizeof(lis->wakeup)); ++ ++ if (__lis302dl_reset_device(lis)) ++ dev_err(lis->dev, "device BOOT reload failed\n"); ++ ++ /* force us powered */ ++ __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | ++ LIS302DL_CTRL1_Xen | ++ LIS302DL_CTRL1_Yen | ++ LIS302DL_CTRL1_Zen); ++ mdelay(1); ++ ++ __reg_write(lis, LIS302DL_REG_CTRL2, 0); ++ __reg_write(lis, LIS302DL_REG_CTRL3, ++ LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL); ++ __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x0); ++ __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00); ++ __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x0); ++ ++ /* start off in powered down mode; we power up when someone opens us */ ++ __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(lis->dev, 1, LIS302DL_INTMODE_GND); ++ __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_GND); ++ ++ __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); ++ local_irq_restore(flags); ++ ++ dev_info(lis->dev, "Found %s\n", pdata->name); ++ ++ lis->pdata = pdata; ++ ++ set_irq_handler(lis->pdata->interrupt, handle_level_irq); ++ ++ rc = request_irq(lis->pdata->interrupt, lis302dl_interrupt, ++ IRQF_TRIGGER_LOW, "lis302dl", lis); ++ ++ if (rc < 0) { ++ dev_err(lis->dev, "error requesting IRQ %d\n", ++ lis->pdata->interrupt); ++ goto bail_inp_reg; ++ } ++ 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(&lis->dev->kobj, &lis302dl_attr_group); ++bail_free_lis: ++ kfree(lis); ++ return rc; ++} ++ ++static int __devexit lis302dl_remove(struct platform_device *pdev) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); ++ unsigned long flags; ++ ++ /* Disable interrupts */ ++ if (lis->flags & LIS302DL_F_IRQ_WAKE) ++ disable_irq_wake(lis->pdata->interrupt); ++ free_irq(lis->pdata->interrupt, lis); ++ ++ /* Reset and power down the device */ ++ local_irq_save(flags); ++ __reg_write(lis, LIS302DL_REG_CTRL3, 0x00); ++ __reg_write(lis, LIS302DL_REG_CTRL2, 0x00); ++ __reg_write(lis, LIS302DL_REG_CTRL1, 0x00); ++ local_irq_restore(flags); ++ ++ /* Cleanup resources */ ++ sysfs_remove_group(&pdev->dev.kobj, &lis302dl_attr_group); ++ input_unregister_device(lis->input_dev); ++ if (lis->input_dev) ++ input_free_device(lis->input_dev); ++ dev_set_drvdata(lis->dev, NULL); ++ kfree(lis); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static u8 regs_to_save[] = { ++ LIS302DL_REG_CTRL2, ++ LIS302DL_REG_CTRL3, ++ LIS302DL_REG_FF_WU_CFG_1, ++ LIS302DL_REG_FF_WU_THS_1, ++ LIS302DL_REG_FF_WU_DURATION_1, ++ LIS302DL_REG_FF_WU_CFG_2, ++ LIS302DL_REG_FF_WU_THS_2, ++ LIS302DL_REG_FF_WU_DURATION_2, ++ LIS302DL_REG_CLICK_CFG, ++ LIS302DL_REG_CLICK_THSY_X, ++ LIS302DL_REG_CLICK_THSZ, ++ LIS302DL_REG_CLICK_TIME_LIMIT, ++ LIS302DL_REG_CLICK_LATENCY, ++ LIS302DL_REG_CLICK_WINDOW, ++ LIS302DL_REG_CTRL1, ++}; ++ ++static int lis302dl_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); ++ unsigned long flags; ++ u_int8_t tmp; ++ int n; ++ ++ /* determine if we want to wake up from the accel. */ ++ if (lis->flags & LIS302DL_F_WUP_CLICK) ++ return 0; ++ ++ disable_irq(lis->pdata->interrupt); ++ local_irq_save(flags); ++ ++ /* ++ * When we share SPI over multiple sensors, there is a race here ++ * that one or more sensors will lose. In that case, the shared ++ * SPI bus GPIO will be in sleep mode and partially pulled down. So ++ * we explicitly put our IO into "wake" mode here before the final ++ * traffic to the sensor. ++ */ ++ (lis->pdata->lis302dl_suspend_io)(lis, 1); ++ ++ /* save registers */ ++ for (n = 0; n < ARRAY_SIZE(regs_to_save); n++) ++ lis->regs[regs_to_save[n]] = ++ __reg_read(lis, regs_to_save[n]); ++ ++ /* power down or enable wakeup */ ++ ++ if (lis->wakeup.threshold == 0) { ++ tmp = __reg_read(lis, LIS302DL_REG_CTRL1); ++ tmp &= ~LIS302DL_CTRL1_PD; ++ __reg_write(lis, LIS302DL_REG_CTRL1, tmp); ++ } else ++ __enable_wakeup(lis); ++ ++ /* place our IO to the device in sleep-compatible states */ ++ (lis->pdata->lis302dl_suspend_io)(lis, 0); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static int lis302dl_resume(struct platform_device *pdev) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev); ++ unsigned long flags; ++ int n; ++ ++ if (lis->flags & LIS302DL_F_WUP_CLICK) ++ return 0; ++ ++ local_irq_save(flags); ++ ++ /* get our IO to the device back in operational states */ ++ (lis->pdata->lis302dl_suspend_io)(lis, 1); ++ ++ /* resume from powerdown first! */ ++ __reg_write(lis, LIS302DL_REG_CTRL1, ++ LIS302DL_CTRL1_PD | ++ LIS302DL_CTRL1_Xen | ++ LIS302DL_CTRL1_Yen | ++ LIS302DL_CTRL1_Zen); ++ mdelay(1); ++ ++ if (__lis302dl_reset_device(lis)) ++ dev_err(&pdev->dev, "device BOOT reload failed\n"); ++ ++ /* restore registers after resume */ ++ for (n = 0; n < ARRAY_SIZE(regs_to_save); n++) ++ __reg_write(lis, regs_to_save[n], lis->regs[regs_to_save[n]]); ++ ++ /* if someone had us open, reset the non-wake threshold stuff */ ++ if (lis->flags & LIS302DL_F_INPUT_OPEN) ++ __enable_data_collection(lis); ++ ++ local_irq_restore(flags); ++ enable_irq(lis->pdata->interrupt); ++ ++ return 0; ++} ++#else ++#define lis302dl_suspend NULL ++#define lis302dl_resume NULL ++#endif ++ ++static struct platform_driver lis302dl_driver = { ++ .driver = { ++ .name = "lis302dl", ++ .owner = THIS_MODULE, ++ }, ++ ++ .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); ++} ++ ++static void __exit lis302dl_exit(void) ++{ ++ platform_driver_unregister(&lis302dl_driver); ++} ++ ++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); ++MODULE_LICENSE("GPL"); ++ ++module_init(lis302dl_init); ++module_exit(lis302dl_exit); +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 4e007c6..c3f53c7 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -475,6 +475,13 @@ config PCH_PHUB + To compile this driver as a module, choose M here: the module will + be called pch_phub. + ++config OPENMOKO_RESUME_REASON ++ tristate "Openmoko resume reason driver" ++ depends on SYSFS ++ help ++ This driver adds a sysfs entry for accessing the resume reason ++ of the openmoko board. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index f546860..702855a 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -43,4 +43,5 @@ obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o + obj-$(CONFIG_PCH_PHUB) += pch_phub.o + obj-y += ti-st/ + obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o ++obj-$(CONFIG_OPENMOKO_RESUME_REASON) += neo1973_pm_resume_reason.o + obj-y += lis3lv02d/ +diff --git a/drivers/misc/neo1973_pm_resume_reason.c b/drivers/misc/neo1973_pm_resume_reason.c +new file mode 100644 +index 0000000..5bcc818 +--- /dev/null ++++ b/drivers/misc/neo1973_pm_resume_reason.c +@@ -0,0 +1,142 @@ ++/* ++ * Resume reason sysfs for the FIC Neo1973 GSM Phone ++ * ++ * (C) 2008 by Openmoko Inc. ++ * Author: Andy Green <andy@openmoko.com> ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License resume_reason 2 as ++ * published by the Free Software Foundation ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++ ++#include <mach/hardware.h> ++#include <asm/mach-types.h> ++ ++#ifdef CONFIG_MACH_NEO1973_GTA02 ++#include <mach/gta02.h> ++#include <linux/mfd/pcf50633/core.h> ++#endif ++ ++static unsigned int *gstatus4_mapped; ++static char *resume_reasons[][17] = { { /* GTA01 */ ++ "EINT00_NULL", ++ "EINT01_GSM", ++ "EINT02_NULL", ++ "EINT03_NULL", ++ "EINT04_JACK", ++ "EINT05_SDCARD", ++ "EINT06_AUXKEY", ++ "EINT07_HOLDKEY", ++ "EINT08_NULL", ++ "EINT09_NULL", ++ "EINT10_NULL", ++ "EINT11_NULL", ++ "EINT12_NULL", ++ "EINT13_NULL", ++ "EINT14_NULL", ++ "EINT15_NULL", ++ NULL ++}, { /* GTA02 */ ++ "EINT00_ACCEL1", ++ "EINT01_GSM", ++ "EINT02_BLUETOOTH", ++ "EINT03_DEBUGBRD", ++ "EINT04_JACK", ++ "EINT05_WLAN", ++ "EINT06_AUXKEY", ++ "EINT07_HOLDKEY", ++ "EINT08_ACCEL2", ++ "EINT09_PMU", ++ "EINT10_NULL", ++ "EINT11_NULL", ++ "EINT12_GLAMO", ++ "EINT13_NULL", ++ "EINT14_NULL", ++ "EINT15_NULL", ++ NULL ++} }; ++ ++static ssize_t resume_reason_read(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int bit = 0; ++ char *end = buf; ++ int gta = !!machine_is_neo1973_gta02(); ++ ++ for (bit = 0; resume_reasons[gta][bit]; bit++) { ++ if ((*gstatus4_mapped) & (1 << bit)) ++ end += sprintf(end, "* %s\n", resume_reasons[gta][bit]); ++ else ++ end += sprintf(end, " %s\n", resume_reasons[gta][bit]); ++ } ++ ++ return end - buf; ++} ++ ++ ++static DEVICE_ATTR(resume_reason, 0644, resume_reason_read, NULL); ++ ++static struct attribute *neo1973_resume_reason_sysfs_entries[] = { ++ &dev_attr_resume_reason.attr, ++ NULL ++}; ++ ++static struct attribute_group neo1973_resume_reason_attr_group = { ++ .name = NULL, ++ .attrs = neo1973_resume_reason_sysfs_entries, ++}; ++ ++static int __init neo1973_resume_reason_probe(struct platform_device *pdev) ++{ ++ dev_info(&pdev->dev, "starting\n"); ++ ++ gstatus4_mapped = ioremap(0x560000BC /* GSTATUS4 */, 0x4); ++ if (!gstatus4_mapped) { ++ dev_err(&pdev->dev, "failed to ioremap() memory region\n"); ++ return -EINVAL; ++ } ++ ++ return sysfs_create_group(&pdev->dev.kobj, ++ &neo1973_resume_reason_attr_group); ++} ++ ++static int neo1973_resume_reason_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, &neo1973_resume_reason_attr_group); ++ iounmap(gstatus4_mapped); ++ return 0; ++} ++ ++static struct platform_driver neo1973_resume_reason_driver = { ++ .probe = neo1973_resume_reason_probe, ++ .remove = neo1973_resume_reason_remove, ++ .driver = { ++ .name = "neo1973-resume", ++ }, ++}; ++ ++static int __devinit neo1973_resume_reason_init(void) ++{ ++ return platform_driver_register(&neo1973_resume_reason_driver); ++} ++ ++static void neo1973_resume_reason_exit(void) ++{ ++ platform_driver_unregister(&neo1973_resume_reason_driver); ++} ++ ++module_init(neo1973_resume_reason_init); ++module_exit(neo1973_resume_reason_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Andy Green <andy@openmoko.com>"); ++MODULE_DESCRIPTION("Neo1973 resume_reason"); +diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c +index 4b530ae..61d233a 100644 +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -79,13 +79,6 @@ struct mmc_blk_data { + + static DEFINE_MUTEX(open_lock); + +-enum mmc_blk_status { +- MMC_BLK_SUCCESS = 0, +- MMC_BLK_RETRY, +- MMC_BLK_DATA_ERR, +- MMC_BLK_CMD_ERR, +-}; +- + module_param(perdev_minors, int, 0444); + MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); + +@@ -172,6 +165,13 @@ static const struct block_device_operations mmc_bdops = { + .owner = THIS_MODULE, + }; + ++struct mmc_blk_request { ++ struct mmc_request mrq; ++ struct mmc_command cmd; ++ struct mmc_command stop; ++ struct mmc_data data; ++}; ++ + static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) + { + int err; +@@ -331,341 +331,200 @@ out: + return err ? 0 : 1; + } + +-static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, +- struct mmc_card *card, +- int disable_multi, +- struct mmc_queue *mq) ++static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) + { +- u32 readcmd, writecmd; +- struct mmc_blk_request *brq = &mqrq->brq; +- struct request *req = mqrq->req; +- +- memset(brq, 0, sizeof(struct mmc_blk_request)); +- +- brq->mrq.cmd = &brq->cmd; +- brq->mrq.data = &brq->data; +- +- brq->cmd.arg = blk_rq_pos(req); +- if (!mmc_card_blockaddr(card)) +- brq->cmd.arg <<= 9; +- brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; +- brq->data.blksz = 512; +- brq->stop.opcode = MMC_STOP_TRANSMISSION; +- brq->stop.arg = 0; +- brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; +- brq->data.blocks = blk_rq_sectors(req); ++ struct mmc_blk_data *md = mq->data; ++ struct mmc_card *card = md->queue.card; ++ struct mmc_blk_request brq; ++ int ret = 1, disable_multi = 0; + +- /* +- * The block layer doesn't support all sector count +- * restrictions, so we need to be prepared for too big +- * requests. +- */ +- if (brq->data.blocks > card->host->max_blk_count) +- brq->data.blocks = card->host->max_blk_count; ++ mmc_claim_host(card->host); + +- /* +- * After a read error, we redo the request one sector at a time +- * in order to accurately determine which sectors can be read +- * successfully. +- */ +- if (disable_multi && brq->data.blocks > 1) +- brq->data.blocks = 1; ++ do { ++ struct mmc_command cmd; ++ u32 readcmd, writecmd, status = 0; ++ ++ memset(&brq, 0, sizeof(struct mmc_blk_request)); ++ brq.mrq.cmd = &brq.cmd; ++ brq.mrq.data = &brq.data; ++ ++ brq.cmd.arg = blk_rq_pos(req); ++ if (!mmc_card_blockaddr(card)) ++ brq.cmd.arg <<= 9; ++ brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; ++ brq.data.blksz = 512; ++ brq.stop.opcode = MMC_STOP_TRANSMISSION; ++ brq.stop.arg = 0; ++ brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; ++ brq.data.blocks = blk_rq_sectors(req); + +- if (brq->data.blocks > 1) { +- /* SPI multiblock writes terminate using a special +- * token, not a STOP_TRANSMISSION request. ++ /* ++ * The block layer doesn't support all sector count ++ * restrictions, so we need to be prepared for too big ++ * requests. + */ +- if (!mmc_host_is_spi(card->host) +- || rq_data_dir(req) == READ) +- brq->mrq.stop = &brq->stop; +- readcmd = MMC_READ_MULTIPLE_BLOCK; +- writecmd = MMC_WRITE_MULTIPLE_BLOCK; +- } else { +- brq->mrq.stop = NULL; +- readcmd = MMC_READ_SINGLE_BLOCK; +- writecmd = MMC_WRITE_BLOCK; +- } +- if (rq_data_dir(req) == READ) { +- brq->cmd.opcode = readcmd; +- brq->data.flags |= MMC_DATA_READ; +- } else { +- brq->cmd.opcode = writecmd; +- brq->data.flags |= MMC_DATA_WRITE; +- } +- +- mmc_set_data_timeout(&brq->data, card); ++ if (brq.data.blocks > card->host->max_blk_count) ++ brq.data.blocks = card->host->max_blk_count; + +- brq->data.sg = mqrq->sg; +- brq->data.sg_len = mmc_queue_map_sg(mq, mqrq); ++ /* ++ * After a read error, we redo the request one sector at a time ++ * in order to accurately determine which sectors can be read ++ * successfully. ++ */ ++ if (disable_multi && brq.data.blocks > 1) ++ brq.data.blocks = 1; + +- /* +- * Adjust the sg list so it is the same size as the +- * request. +- */ +- if (brq->data.blocks != blk_rq_sectors(req)) { +- int i, data_size = brq->data.blocks << 9; +- struct scatterlist *sg; +- +- for_each_sg(brq->data.sg, sg, brq->data.sg_len, i) { +- data_size -= sg->length; +- if (data_size <= 0) { +- sg->length += data_size; +- i++; +- break; +- } ++ if (brq.data.blocks > 1) { ++ /* SPI multiblock writes terminate using a special ++ * token, not a STOP_TRANSMISSION request. ++ */ ++ if (!mmc_host_is_spi(card->host) ++ || rq_data_dir(req) == READ) ++ brq.mrq.stop = &brq.stop; ++ readcmd = MMC_READ_MULTIPLE_BLOCK; ++ writecmd = MMC_WRITE_MULTIPLE_BLOCK; ++ } else { ++ brq.mrq.stop = NULL; ++ readcmd = MMC_READ_SINGLE_BLOCK; ++ writecmd = MMC_WRITE_BLOCK; + } +- brq->data.sg_len = i; +- } +- +- mmc_queue_bounce_pre(mqrq); +-} +- +-static enum mmc_blk_status mmc_blk_get_status(struct mmc_blk_request *brq, +- struct request *req, +- struct mmc_card *card, +- struct mmc_blk_data *md) +-{ +- struct mmc_command cmd; +- u32 status; +- enum mmc_blk_status ret = MMC_BLK_SUCCESS; +- +- /* +- * Check for errors here, but don't jump to cmd_err +- * until later as we need to wait for the card to leave +- * programming mode even when things go wrong. +- */ +- if (brq->cmd.error || brq->data.error || brq->stop.error) { +- if (brq->data.blocks > 1 && rq_data_dir(req) == READ) { +- /* Redo read one sector at a time */ +- printk(KERN_WARNING "%s: retrying using single " +- "block read, brq %p\n", +- req->rq_disk->disk_name, brq); +- ret = MMC_BLK_RETRY; +- goto out; ++ if (rq_data_dir(req) == READ) { ++ brq.cmd.opcode = readcmd; ++ brq.data.flags |= MMC_DATA_READ; ++ } else { ++ brq.cmd.opcode = writecmd; ++ brq.data.flags |= MMC_DATA_WRITE; + } +- status = get_card_status(card, req); +- } + +- if (brq->cmd.error) { +- printk(KERN_ERR "%s: error %d sending read/write " +- "command, response %#x, card status %#x\n", +- req->rq_disk->disk_name, brq->cmd.error, +- brq->cmd.resp[0], status); +- } +- +- if (brq->data.error) { +- if (brq->data.error == -ETIMEDOUT && brq->mrq.stop) +- /* 'Stop' response contains card status */ +- status = brq->mrq.stop->resp[0]; +- printk(KERN_ERR "%s: error %d transferring data," +- " sector %u, nr %u, card status %#x\n", +- req->rq_disk->disk_name, brq->data.error, +- (unsigned)blk_rq_pos(req), +- (unsigned)blk_rq_sectors(req), status); +- } ++ mmc_set_data_timeout(&brq.data, card); + +- if (brq->stop.error) { +- printk(KERN_ERR "%s: error %d sending stop command, " +- "response %#x, card status %#x\n", +- req->rq_disk->disk_name, brq->stop.error, +- brq->stop.resp[0], status); +- } ++ brq.data.sg = mq->sg; ++ brq.data.sg_len = mmc_queue_map_sg(mq); + +- if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { +- do { +- int err; +- +- cmd.opcode = MMC_SEND_STATUS; +- cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; +- err = mmc_wait_for_cmd(card->host, &cmd, 5); +- if (err) { +- printk(KERN_ERR "%s: error %d requesting status\n", +- req->rq_disk->disk_name, err); +- ret = MMC_BLK_CMD_ERR; +- goto out; ++ /* ++ * Adjust the sg list so it is the same size as the ++ * request. ++ */ ++ if (brq.data.blocks != blk_rq_sectors(req)) { ++ int i, data_size = brq.data.blocks << 9; ++ struct scatterlist *sg; ++ ++ for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) { ++ data_size -= sg->length; ++ if (data_size <= 0) { ++ sg->length += data_size; ++ i++; ++ break; ++ } + } +- /* +- * Some cards mishandle the status bits, +- * so make sure to check both the busy +- * indication and the card state. +- */ +- } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || +- (R1_CURRENT_STATE(cmd.resp[0]) == 7)); +- +-#if 0 +- if (cmd.resp[0] & ~0x00000900) +- printk(KERN_ERR "%s: status = %08x\n", +- req->rq_disk->disk_name, cmd.resp[0]); +- if (mmc_decode_status(cmd.resp)) { +- ret = MMC_BLK_CMD_ERR; +- goto out; ++ brq.data.sg_len = i; + } + +-#endif +- } ++ mmc_queue_bounce_pre(mq); + +- if (brq->cmd.error || brq->stop.error || brq->data.error) { +- if (rq_data_dir(req) == READ) +- ret = MMC_BLK_DATA_ERR; +- else +- ret = MMC_BLK_CMD_ERR; +- } +- out: +- return ret; ++ mmc_wait_for_req(card->host, &brq.mrq); + +-} ++ mmc_queue_bounce_post(mq); + +-static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) +-{ +- struct mmc_blk_data *md = mq->data; +- struct mmc_card *card = md->queue.card; +- struct mmc_blk_request *brqc = &mq->mqrq_cur->brq; +- struct mmc_blk_request *brqp = &mq->mqrq_prev->brq; +- struct mmc_queue_req *mqrqp = mq->mqrq_prev; +- struct request *rqp = mqrqp->req; +- int ret = 0; +- int disable_multi = 0; +- enum mmc_blk_status status; +- +- if (!rqc && !rqp) +- return 0; +- +- if (rqc) { +- /* Claim host for the first request in a serie of requests */ +- if (!rqp) +- mmc_claim_host(card->host); +- +- /* Prepare a new request */ +- mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq); +- mmc_pre_req(card->host, &brqc->mrq, !rqp); +- } +- do { + /* +- * If there is an ongoing request, indicated by rqp, wait for +- * it to finish before starting a new one. ++ * Check for errors here, but don't jump to cmd_err ++ * until later as we need to wait for the card to leave ++ * programming mode even when things go wrong. + */ +- if (rqp) +- mmc_wait_for_req_done(&brqp->mrq); +- else { +- /* start a new asynchronous request */ +- mmc_start_req(card->host, &brqc->mrq); +- goto out; +- } +- status = mmc_blk_get_status(brqp, rqp, card, md); +- if (status != MMC_BLK_SUCCESS) { +- mmc_post_req(card->host, &brqp->mrq, -EINVAL); +- mmc_queue_bounce_post(mqrqp); +- if (rqc) +- mmc_post_req(card->host, &brqc->mrq, -EINVAL); ++ if (brq.cmd.error || brq.data.error || brq.stop.error) { ++ if (brq.data.blocks > 1 && rq_data_dir(req) == READ) { ++ /* Redo read one sector at a time */ ++ printk(KERN_WARNING "%s: retrying using single " ++ "block read\n", req->rq_disk->disk_name); ++ disable_multi = 1; ++ continue; ++ } ++ status = get_card_status(card, req); + } + +- switch (status) { +- case MMC_BLK_SUCCESS: +- /* +- * A block was successfully transferred. +- */ +- +- /* +- * All data is transferred without errors. +- * Defer mmc post processing and _blk_end_request +- * until after the new request is started. +- */ +- if (blk_rq_bytes(rqp) == brqp->data.bytes_xfered) +- break; +- +- mmc_post_req(card->host, &brqp->mrq, 0); +- mmc_queue_bounce_post(mqrqp); +- +- spin_lock_irq(&md->lock); +- ret = __blk_end_request(rqp, 0, +- brqp->data.bytes_xfered); +- spin_unlock_irq(&md->lock); +- +- if (rqc) +- mmc_post_req(card->host, &brqc->mrq, -EINVAL); +- break; +- case MMC_BLK_CMD_ERR: +- goto cmd_err; +- break; +- case MMC_BLK_RETRY: +- disable_multi = 1; +- ret = 1; +- break; +- case MMC_BLK_DATA_ERR: +- /* +- * After an error, we redo I/O one sector at a +- * time, so we only reach here after trying to +- * read a single sector. +- */ +- spin_lock_irq(&md->lock); +- ret = __blk_end_request(rqp, -EIO, brqp->data.blksz); +- spin_unlock_irq(&md->lock); +- if (rqc && !ret) +- mmc_pre_req(card->host, &brqc->mrq, false); +- break; ++ if (brq.cmd.error) { ++ printk(KERN_ERR "%s: error %d sending read/write " ++ "command, response %#x, card status %#x\n", ++ req->rq_disk->disk_name, brq.cmd.error, ++ brq.cmd.resp[0], status); + } + +- if (ret) { +- /* +- * In case of a none complete request +- * prepare it again and resend. +- */ +- mmc_blk_rw_rq_prep(mqrqp, card, disable_multi, mq); +- mmc_pre_req(card->host, &brqp->mrq, true); +- mmc_start_req(card->host, &brqp->mrq); +- if (rqc) +- mmc_pre_req(card->host, &brqc->mrq, false); ++ if (brq.data.error) { ++ if (brq.data.error == -ETIMEDOUT && brq.mrq.stop) ++ /* 'Stop' response contains card status */ ++ status = brq.mrq.stop->resp[0]; ++ printk(KERN_ERR "%s: error %d transferring data," ++ " sector %u, nr %u, card status %#x\n", ++ req->rq_disk->disk_name, brq.data.error, ++ (unsigned)blk_rq_pos(req), ++ (unsigned)blk_rq_sectors(req), status); + } +- } while (ret); + +- /* Previous request is completed, start the new request if any */ +- if (rqc) +- mmc_start_req(card->host, &brqc->mrq); ++ if (brq.stop.error) { ++ printk(KERN_ERR "%s: error %d sending stop command, " ++ "response %#x, card status %#x\n", ++ req->rq_disk->disk_name, brq.stop.error, ++ brq.stop.resp[0], status); ++ } + +- /* +- * Post process the previous request while the new request is active. +- * In case of error the reuqest is already ended. +- */ +- if (status == MMC_BLK_SUCCESS) { +- mmc_post_req(card->host, &brqp->mrq, 0); +- mmc_queue_bounce_post(mqrqp); ++ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { ++ do { ++ int err; ++ ++ cmd.opcode = MMC_SEND_STATUS; ++ cmd.arg = card->rca << 16; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ err = mmc_wait_for_cmd(card->host, &cmd, 5); ++ if (err) { ++ printk(KERN_ERR "%s: error %d requesting status\n", ++ req->rq_disk->disk_name, err); ++ goto cmd_err; ++ } ++ /* ++ * Some cards mishandle the status bits, ++ * so make sure to check both the busy ++ * indication and the card state. ++ */ ++ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || ++ (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + +- spin_lock_irq(&md->lock); +- ret = __blk_end_request(rqp, 0, brqp->data.bytes_xfered); +- spin_unlock_irq(&md->lock); ++#if 0 ++ if (cmd.resp[0] & ~0x00000900) ++ printk(KERN_ERR "%s: status = %08x\n", ++ req->rq_disk->disk_name, cmd.resp[0]); ++ if (mmc_decode_status(cmd.resp)) ++ goto cmd_err; ++#endif ++ } + +- if (ret) { +- /* If this happen it is a bug */ +- printk(KERN_ERR "[%s] BUG: rq_bytes %d xfered %d\n", +- __func__, blk_rq_bytes(rqp), +- brqp->data.bytes_xfered); ++ if (brq.cmd.error || brq.stop.error || brq.data.error) { ++ if (rq_data_dir(req) == READ) { ++ /* ++ * After an error, we redo I/O one sector at a ++ * time, so we only reach here after trying to ++ * read a single sector. ++ */ ++ spin_lock_irq(&md->lock); ++ ret = __blk_end_request(req, -EIO, brq.data.blksz); ++ spin_unlock_irq(&md->lock); ++ continue; ++ } + goto cmd_err; + } +- } + +- /* 1 indicates one request has been completed */ +- ret = 1; +- out: +- /* +- * TODO: Find out if it is OK to only release host after the +- * last request. For the last request the current request +- * is NULL, which means no requests are pending. +- */ +- /* Release host for the last request in a serie of requests */ +- if (!rqc) +- mmc_release_host(card->host); ++ /* ++ * A block was successfully transferred. ++ */ ++ spin_lock_irq(&md->lock); ++ ret = __blk_end_request(req, 0, brq.data.bytes_xfered); ++ spin_unlock_irq(&md->lock); ++ } while (ret); + +- /* Current request becomes previous request and vice versa. */ +- mqrqp->brq.mrq.data = NULL; +- mqrqp->req = NULL; +- mq->mqrq_prev = mq->mqrq_cur; +- mq->mqrq_cur = mqrqp; ++ mmc_release_host(card->host); + +- return ret; ++ return 1; + + cmd_err: +- + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. +@@ -680,12 +539,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + spin_lock_irq(&md->lock); +- ret = __blk_end_request(rqp, 0, blocks << 9); ++ ret = __blk_end_request(req, 0, blocks << 9); + spin_unlock_irq(&md->lock); + } + } else { + spin_lock_irq(&md->lock); +- ret = __blk_end_request(rqp, 0, brqp->data.bytes_xfered); ++ ret = __blk_end_request(req, 0, brq.data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + +@@ -693,27 +552,15 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) + + spin_lock_irq(&md->lock); + while (ret) +- ret = __blk_end_request(rqp, -EIO, blk_rq_cur_bytes(rqp)); ++ ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req)); + spin_unlock_irq(&md->lock); + +- if (rqc) { +- mmc_claim_host(card->host); +- mmc_pre_req(card->host, &brqc->mrq, false); +- mmc_start_req(card->host, &brqc->mrq); +- } +- +- /* Current request becomes previous request and vice versa. */ +- mqrqp->brq.mrq.data = NULL; +- mqrqp->req = NULL; +- mq->mqrq_prev = mq->mqrq_cur; +- mq->mqrq_cur = mqrqp; +- + return 0; + } + + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) + { +- if (req && req->cmd_flags & REQ_DISCARD) { ++ if (req->cmd_flags & REQ_DISCARD) { + if (req->cmd_flags & REQ_SECURE) + return mmc_blk_issue_secdiscard_rq(mq, req); + else +diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c +index 8f7ffad..abc1a63 100644 +--- a/drivers/mmc/card/mmc_test.c ++++ b/drivers/mmc/card/mmc_test.c +@@ -22,7 +22,6 @@ + #include <linux/debugfs.h> + #include <linux/uaccess.h> + #include <linux/seq_file.h> +-#include <linux/random.h> + + #define RESULT_OK 0 + #define RESULT_FAIL 1 +@@ -52,12 +51,10 @@ struct mmc_test_pages { + * struct mmc_test_mem - allocated memory. + * @arr: array of allocations + * @cnt: number of allocations +- * @size_min_cmn: lowest common size in array of allocations + */ + struct mmc_test_mem { + struct mmc_test_pages *arr; + unsigned int cnt; +- unsigned int size_min_cmn; + }; + + /** +@@ -151,21 +148,6 @@ struct mmc_test_card { + struct mmc_test_general_result *gr; + }; + +-enum mmc_test_prep_media { +- MMC_TEST_PREP_NONE = 0, +- MMC_TEST_PREP_WRITE_FULL = 1 << 0, +- MMC_TEST_PREP_ERASE = 1 << 1, +-}; +- +-struct mmc_test_multiple_rw { +- unsigned int *bs; +- unsigned int len; +- unsigned int size; +- bool do_write; +- bool do_nonblock_req; +- enum mmc_test_prep_media prepare; +-}; +- + /*******************************************************************/ + /* General helper functions */ + /*******************************************************************/ +@@ -325,7 +307,6 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, + unsigned long max_seg_page_cnt = DIV_ROUND_UP(max_seg_sz, PAGE_SIZE); + unsigned long page_cnt = 0; + unsigned long limit = nr_free_buffer_pages() >> 4; +- unsigned int min_cmn = 0; + struct mmc_test_mem *mem; + + if (max_page_cnt > limit) +@@ -369,12 +350,6 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, + mem->arr[mem->cnt].page = page; + mem->arr[mem->cnt].order = order; + mem->cnt += 1; +- if (!min_cmn) +- min_cmn = PAGE_SIZE << order; +- else +- min_cmn = min(min_cmn, +- (unsigned int) (PAGE_SIZE << order)); +- + if (max_page_cnt <= (1UL << order)) + break; + max_page_cnt -= 1UL << order; +@@ -385,7 +360,6 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, + break; + } + } +- mem->size_min_cmn = min_cmn; + + return mem; + +@@ -412,6 +386,7 @@ static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz, + do { + for (i = 0; i < mem->cnt; i++) { + unsigned long len = PAGE_SIZE << mem->arr[i].order; ++ + if (len > sz) + len = sz; + if (len > max_seg_sz) +@@ -750,94 +725,6 @@ static int mmc_test_check_broken_result(struct mmc_test_card *test, + } + + /* +- * Tests nonblock transfer with certain parameters +- */ +-static void mmc_test_nonblock_reset(struct mmc_request *mrq, +- struct mmc_command *cmd, +- struct mmc_command *stop, +- struct mmc_data *data) +-{ +- memset(mrq, 0, sizeof(struct mmc_request)); +- memset(cmd, 0, sizeof(struct mmc_command)); +- memset(data, 0, sizeof(struct mmc_data)); +- memset(stop, 0, sizeof(struct mmc_command)); +- +- mrq->cmd = cmd; +- mrq->data = data; +- mrq->stop = stop; +-} +-static int mmc_test_nonblock_transfer(struct mmc_test_card *test, +- struct scatterlist *sg, unsigned sg_len, +- unsigned dev_addr, unsigned blocks, +- unsigned blksz, int write, int count) +-{ +- struct mmc_request mrq1; +- struct mmc_command cmd1; +- struct mmc_command stop1; +- struct mmc_data data1; +- +- struct mmc_request mrq2; +- struct mmc_command cmd2; +- struct mmc_command stop2; +- struct mmc_data data2; +- +- struct mmc_request *cur_mrq; +- struct mmc_request *prev_mrq; +- int i; +- int ret = 0; +- +- if (!test->card->host->ops->pre_req || +- !test->card->host->ops->post_req) +- return -RESULT_UNSUP_HOST; +- +- mmc_test_nonblock_reset(&mrq1, &cmd1, &stop1, &data1); +- mmc_test_nonblock_reset(&mrq2, &cmd2, &stop2, &data2); +- +- cur_mrq = &mrq1; +- prev_mrq = NULL; +- +- for (i = 0; i < count; i++) { +- mmc_test_prepare_mrq(test, cur_mrq, sg, sg_len, dev_addr, +- blocks, blksz, write); +- mmc_pre_req(test->card->host, cur_mrq, !prev_mrq); +- +- if (prev_mrq) { +- mmc_wait_for_req_done(prev_mrq); +- mmc_test_wait_busy(test); +- ret = mmc_test_check_result(test, prev_mrq); +- if (ret) +- goto err; +- } +- +- mmc_start_req(test->card->host, cur_mrq); +- +- if (prev_mrq) +- mmc_post_req(test->card->host, prev_mrq, 0); +- +- prev_mrq = cur_mrq; +- if (cur_mrq == &mrq1) { +- mmc_test_nonblock_reset(&mrq2, &cmd2, &stop2, &data2); +- cur_mrq = &mrq2; +- } else { +- mmc_test_nonblock_reset(&mrq1, &cmd1, &stop1, &data1); +- cur_mrq = &mrq1; +- } +- dev_addr += blocks; +- } +- +- mmc_wait_for_req_done(prev_mrq); +- mmc_test_wait_busy(test); +- ret = mmc_test_check_result(test, prev_mrq); +- if (ret) +- goto err; +- mmc_post_req(test->card->host, prev_mrq, 0); +- +- return ret; +-err: +- return ret; +-} +- +-/* + * Tests a basic transfer with certain parameters + */ + static int mmc_test_simple_transfer(struct mmc_test_card *test, +@@ -1464,17 +1351,14 @@ static int mmc_test_area_transfer(struct mmc_test_card *test, + } + + /* +- * Map and transfer bytes for multiple transfers. ++ * Map and transfer bytes. + */ +-static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, +- unsigned int dev_addr, int write, +- int max_scatter, int timed, int count, +- bool nonblock) ++static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz, ++ unsigned int dev_addr, int write, int max_scatter, ++ int timed) + { + struct timespec ts1, ts2; +- int ret = 0; +- int i; +- struct mmc_test_area *t = &test->area; ++ int ret; + + /* + * In the case of a maximally scattered transfer, the maximum transfer +@@ -1498,15 +1382,8 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, + + if (timed) + getnstimeofday(&ts1); +- if (nonblock) +- ret = mmc_test_nonblock_transfer(test, t->sg, t->sg_len, +- dev_addr, t->blocks, 512, write, count); +- else +- for (i = 0; i < count && ret == 0; i++) { +- ret = mmc_test_area_transfer(test, dev_addr, write); +- dev_addr += sz >> 9; +- } + ++ ret = mmc_test_area_transfer(test, dev_addr, write); + if (ret) + return ret; + +@@ -1514,19 +1391,11 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, + getnstimeofday(&ts2); + + if (timed) +- mmc_test_print_avg_rate(test, sz, count, &ts1, &ts2); ++ mmc_test_print_rate(test, sz, &ts1, &ts2); + + return 0; + } + +-static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz, +- unsigned int dev_addr, int write, int max_scatter, +- int timed) +-{ +- return mmc_test_area_io_seq(test, sz, dev_addr, write, max_scatter, +- timed, 1, false); +-} +- + /* + * Write the test area entirely. + */ +@@ -2087,144 +1956,6 @@ static int mmc_test_large_seq_write_perf(struct mmc_test_card *test) + return mmc_test_large_seq_perf(test, 1); + } + +-static int mmc_test_rw_multiple(struct mmc_test_card *test, +- struct mmc_test_multiple_rw *tdata, +- unsigned int reqsize, unsigned int size) +-{ +- unsigned int dev_addr; +- struct mmc_test_area *t = &test->area; +- int ret = 0; +- int max_reqsize = max(t->mem->size_min_cmn * +- min(t->max_segs, t->mem->cnt), t->max_tfr); +- +- /* Set up test area */ +- if (size > mmc_test_capacity(test->card) / 2 * 512) +- size = mmc_test_capacity(test->card) / 2 * 512; +- if (reqsize > max_reqsize) +- reqsize = max_reqsize; +- dev_addr = mmc_test_capacity(test->card) / 4; +- if ((dev_addr & 0xffff0000)) +- dev_addr &= 0xffff0000; /* Round to 64MiB boundary */ +- else +- dev_addr &= 0xfffff800; /* Round to 1MiB boundary */ +- if (!dev_addr) +- goto err; +- +- /* prepare test area */ +- if (mmc_can_erase(test->card) && +- tdata->prepare & MMC_TEST_PREP_ERASE) { +- ret = mmc_erase(test->card, dev_addr, +- size / 512, MMC_SECURE_ERASE_ARG); +- if (ret) +- ret = mmc_erase(test->card, dev_addr, +- size / 512, MMC_ERASE_ARG); +- if (ret) +- goto err; +- } +- +- /* Run test */ +- ret = mmc_test_area_io_seq(test, reqsize, dev_addr, +- tdata->do_write, 0, 1, size / reqsize, +- tdata->do_nonblock_req); +- if (ret) +- goto err; +- +- return ret; +- err: +- printk(KERN_INFO "[%s] error\n", __func__); +- return ret; +-} +- +-static int mmc_test_rw_multiple_size(struct mmc_test_card *test, +- struct mmc_test_multiple_rw *rw) +-{ +- int ret = 0; +- int i; +- +- for (i = 0 ; i < rw->len && ret == 0; i++) { +- ret = mmc_test_rw_multiple(test, rw, rw->bs[i], rw->size); +- if (ret) +- break; +- } +- return ret; +-} +- +-/* +- * Multiple blocking write 4k to 4 MB chunks +- */ +-static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test) +-{ +- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, +- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; +- struct mmc_test_multiple_rw test_data = { +- .bs = bs, +- .size = 128*1024*1024, +- .len = ARRAY_SIZE(bs), +- .do_write = true, +- .do_nonblock_req = false, +- .prepare = MMC_TEST_PREP_ERASE, +- }; +- +- return mmc_test_rw_multiple_size(test, &test_data); +-}; +- +-/* +- * Multiple none blocking write 4k to 4 MB chunks +- */ +-static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test) +-{ +- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, +- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; +- struct mmc_test_multiple_rw test_data = { +- .bs = bs, +- .size = 128*1024*1024, +- .len = ARRAY_SIZE(bs), +- .do_write = true, +- .do_nonblock_req = true, +- .prepare = MMC_TEST_PREP_ERASE, +- }; +- +- return mmc_test_rw_multiple_size(test, &test_data); +-} +- +-/* +- * Multiple blocking read 4k to 4 MB chunks +- */ +-static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test) +-{ +- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, +- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; +- struct mmc_test_multiple_rw test_data = { +- .bs = bs, +- .size = 128*1024*1024, +- .len = ARRAY_SIZE(bs), +- .do_write = false, +- .do_nonblock_req = false, +- .prepare = MMC_TEST_PREP_NONE, +- }; +- +- return mmc_test_rw_multiple_size(test, &test_data); +-} +- +-/* +- * Multiple none blocking read 4k to 4 MB chunks +- */ +-static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test) +-{ +- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, +- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; +- struct mmc_test_multiple_rw test_data = { +- .bs = bs, +- .size = 128*1024*1024, +- .len = ARRAY_SIZE(bs), +- .do_write = false, +- .do_nonblock_req = true, +- .prepare = MMC_TEST_PREP_NONE, +- }; +- +- return mmc_test_rw_multiple_size(test, &test_data); +-} +- + static const struct mmc_test_case mmc_test_cases[] = { + { + .name = "Basic write (no data verification)", +@@ -2492,33 +2223,6 @@ static const struct mmc_test_case mmc_test_cases[] = { + .cleanup = mmc_test_area_cleanup, + }, + +- { +- .name = "Write performance with blocking req 4k to 4MB", +- .prepare = mmc_test_area_prepare, +- .run = mmc_test_profile_mult_write_blocking_perf, +- .cleanup = mmc_test_area_cleanup, +- }, +- +- { +- .name = "Write performance with none blocking req 4k to 4MB", +- .prepare = mmc_test_area_prepare, +- .run = mmc_test_profile_mult_write_nonblock_perf, +- .cleanup = mmc_test_area_cleanup, +- }, +- +- { +- .name = "Read performance with blocking req 4k to 4MB", +- .prepare = mmc_test_area_prepare, +- .run = mmc_test_profile_mult_read_blocking_perf, +- .cleanup = mmc_test_area_cleanup, +- }, +- +- { +- .name = "Read performance with none blocking req 4k to 4MB", +- .prepare = mmc_test_area_prepare, +- .run = mmc_test_profile_mult_read_nonblock_perf, +- .cleanup = mmc_test_area_cleanup, +- }, + }; + + static DEFINE_MUTEX(mmc_test_lock); +@@ -2743,32 +2447,6 @@ static const struct file_operations mmc_test_fops_test = { + .release = single_release, + }; + +-static int mtf_testlist_show(struct seq_file *sf, void *data) +-{ +- int i; +- +- mutex_lock(&mmc_test_lock); +- +- for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++) +- seq_printf(sf, "%d:\t%s\n", i+1, mmc_test_cases[i].name); +- +- mutex_unlock(&mmc_test_lock); +- +- return 0; +-} +- +-static int mtf_testlist_open(struct inode *inode, struct file *file) +-{ +- return single_open(file, mtf_testlist_show, inode->i_private); +-} +- +-static const struct file_operations mmc_test_fops_testlist = { +- .open = mtf_testlist_open, +- .read = seq_read, +- .llseek = seq_lseek, +- .release = single_release, +-}; +- + static void mmc_test_free_file_test(struct mmc_card *card) + { + struct mmc_test_dbgfs_file *df, *dfs; +@@ -2798,10 +2476,6 @@ static int mmc_test_register_file_test(struct mmc_card *card) + file = debugfs_create_file("test", S_IWUSR | S_IRUGO, + card->debugfs_root, card, &mmc_test_fops_test); + +- if (card->debugfs_root) +- file = debugfs_create_file("testlist", S_IRUGO, +- card->debugfs_root, card, &mmc_test_fops_testlist); +- + if (IS_ERR_OR_NULL(file)) { + dev_err(&card->dev, + "Can't create file. Perhaps debugfs is disabled.\n"); +diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c +index 2b14d1c..2ae7275 100644 +--- a/drivers/mmc/card/queue.c ++++ b/drivers/mmc/card/queue.c +@@ -56,10 +56,9 @@ static int mmc_queue_thread(void *d) + spin_lock_irq(q->queue_lock); + set_current_state(TASK_INTERRUPTIBLE); + req = blk_fetch_request(q); +- mq->mqrq_cur->req = req; ++ mq->req = req; + spin_unlock_irq(q->queue_lock); + +- mq->issue_fn(mq, req); + if (!req) { + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); +@@ -72,6 +71,7 @@ static int mmc_queue_thread(void *d) + } + set_current_state(TASK_RUNNING); + ++ mq->issue_fn(mq, req); + } while (1); + up(&mq->thread_sem); + +@@ -97,25 +97,10 @@ static void mmc_request(struct request_queue *q) + return; + } + +- if (!mq->mqrq_cur->req) ++ if (!mq->req) + wake_up_process(mq->thread); + } + +-struct scatterlist *mmc_alloc_sg(int sg_len, int *err) +-{ +- struct scatterlist *sg; +- +- sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL); +- if (!sg) +- *err = -ENOMEM; +- else { +- *err = 0; +- sg_init_table(sg, sg_len); +- } +- +- return sg; +-} +- + /** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue +@@ -129,8 +114,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock + struct mmc_host *host = card->host; + u64 limit = BLK_BOUNCE_HIGH; + int ret; +- struct mmc_queue_req *mqrq_cur = &mq->mqrq[0]; +- struct mmc_queue_req *mqrq_prev = &mq->mqrq[1]; + + if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) + limit = *mmc_dev(host)->dma_mask; +@@ -140,11 +123,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock + if (!mq->queue) + return -ENOMEM; + +- memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur)); +- memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev)); +- mq->mqrq_cur = mqrq_cur; +- mq->mqrq_prev = mqrq_prev; + mq->queue->queuedata = mq; ++ mq->req = NULL; + + blk_queue_prep_rq(mq->queue, mmc_prep_request); + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue); +@@ -178,64 +158,53 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock + bouncesz = host->max_blk_count * 512; + + if (bouncesz > 512) { +- mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); +- if (!mqrq_cur->bounce_buf) { +- printk(KERN_WARNING "%s: unable to " +- "allocate bounce cur buffer\n", +- mmc_card_name(card)); +- } +- mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); +- if (!mqrq_prev->bounce_buf) { ++ mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); ++ if (!mq->bounce_buf) { + printk(KERN_WARNING "%s: unable to " +- "allocate bounce prev buffer\n", ++ "allocate bounce buffer\n", + mmc_card_name(card)); +- kfree(mqrq_cur->bounce_buf); +- mqrq_cur->bounce_buf = NULL; + } + } + +- if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf) { ++ if (mq->bounce_buf) { + blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY); + blk_queue_max_hw_sectors(mq->queue, bouncesz / 512); + blk_queue_max_segments(mq->queue, bouncesz / 512); + blk_queue_max_segment_size(mq->queue, bouncesz); + +- mqrq_cur->sg = mmc_alloc_sg(1, &ret); +- if (ret) +- goto cleanup_queue; +- +- mqrq_cur->bounce_sg = +- mmc_alloc_sg(bouncesz / 512, &ret); +- if (ret) +- goto cleanup_queue; +- +- mqrq_prev->sg = mmc_alloc_sg(1, &ret); +- if (ret) ++ mq->sg = kmalloc(sizeof(struct scatterlist), ++ GFP_KERNEL); ++ if (!mq->sg) { ++ ret = -ENOMEM; + goto cleanup_queue; ++ } ++ sg_init_table(mq->sg, 1); + +- mqrq_prev->bounce_sg = +- mmc_alloc_sg(bouncesz / 512, &ret); +- if (ret) ++ mq->bounce_sg = kmalloc(sizeof(struct scatterlist) * ++ bouncesz / 512, GFP_KERNEL); ++ if (!mq->bounce_sg) { ++ ret = -ENOMEM; + goto cleanup_queue; ++ } ++ sg_init_table(mq->bounce_sg, bouncesz / 512); + } + } + #endif + +- if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) { ++ if (!mq->bounce_buf) { + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_hw_sectors(mq->queue, + min(host->max_blk_count, host->max_req_size / 512)); + blk_queue_max_segments(mq->queue, host->max_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + +- mqrq_cur->sg = mmc_alloc_sg(host->max_segs, &ret); +- if (ret) +- goto cleanup_queue; +- +- +- mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret); +- if (ret) ++ mq->sg = kmalloc(sizeof(struct scatterlist) * ++ host->max_segs, GFP_KERNEL); ++ if (!mq->sg) { ++ ret = -ENOMEM; + goto cleanup_queue; ++ } ++ sg_init_table(mq->sg, host->max_segs); + } + + sema_init(&mq->thread_sem, 1); +@@ -250,22 +219,16 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock + + return 0; + free_bounce_sg: +- kfree(mqrq_cur->bounce_sg); +- mqrq_cur->bounce_sg = NULL; +- kfree(mqrq_prev->bounce_sg); +- mqrq_prev->bounce_sg = NULL; +- ++ if (mq->bounce_sg) ++ kfree(mq->bounce_sg); ++ mq->bounce_sg = NULL; + cleanup_queue: +- kfree(mqrq_cur->sg); +- mqrq_cur->sg = NULL; +- kfree(mqrq_cur->bounce_buf); +- mqrq_cur->bounce_buf = NULL; +- +- kfree(mqrq_prev->sg); +- mqrq_prev->sg = NULL; +- kfree(mqrq_prev->bounce_buf); +- mqrq_prev->bounce_buf = NULL; +- ++ if (mq->sg) ++ kfree(mq->sg); ++ mq->sg = NULL; ++ if (mq->bounce_buf) ++ kfree(mq->bounce_buf); ++ mq->bounce_buf = NULL; + blk_cleanup_queue(mq->queue); + return ret; + } +@@ -274,8 +237,6 @@ void mmc_cleanup_queue(struct mmc_queue *mq) + { + struct request_queue *q = mq->queue; + unsigned long flags; +- struct mmc_queue_req *mqrq_cur = mq->mqrq_cur; +- struct mmc_queue_req *mqrq_prev = mq->mqrq_prev; + + /* Make sure the queue isn't suspended, as that will deadlock */ + mmc_queue_resume(mq); +@@ -289,23 +250,16 @@ void mmc_cleanup_queue(struct mmc_queue *mq) + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + +- kfree(mqrq_cur->bounce_sg); +- mqrq_cur->bounce_sg = NULL; +- +- kfree(mqrq_cur->sg); +- mqrq_cur->sg = NULL; ++ if (mq->bounce_sg) ++ kfree(mq->bounce_sg); ++ mq->bounce_sg = NULL; + +- kfree(mqrq_cur->bounce_buf); +- mqrq_cur->bounce_buf = NULL; ++ kfree(mq->sg); ++ mq->sg = NULL; + +- kfree(mqrq_prev->bounce_sg); +- mqrq_prev->bounce_sg = NULL; +- +- kfree(mqrq_prev->sg); +- mqrq_prev->sg = NULL; +- +- kfree(mqrq_prev->bounce_buf); +- mqrq_prev->bounce_buf = NULL; ++ if (mq->bounce_buf) ++ kfree(mq->bounce_buf); ++ mq->bounce_buf = NULL; + + mq->card = NULL; + } +@@ -358,27 +312,27 @@ void mmc_queue_resume(struct mmc_queue *mq) + /* + * Prepare the sg list(s) to be handed of to the host driver + */ +-unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq) ++unsigned int mmc_queue_map_sg(struct mmc_queue *mq) + { + unsigned int sg_len; + size_t buflen; + struct scatterlist *sg; + int i; + +- if (!mqrq->bounce_buf) +- return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg); ++ if (!mq->bounce_buf) ++ return blk_rq_map_sg(mq->queue, mq->req, mq->sg); + +- BUG_ON(!mqrq->bounce_sg); ++ BUG_ON(!mq->bounce_sg); + +- sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg); ++ sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg); + +- mqrq->bounce_sg_len = sg_len; ++ mq->bounce_sg_len = sg_len; + + buflen = 0; +- for_each_sg(mqrq->bounce_sg, sg, sg_len, i) ++ for_each_sg(mq->bounce_sg, sg, sg_len, i) + buflen += sg->length; + +- sg_init_one(mqrq->sg, mqrq->bounce_buf, buflen); ++ sg_init_one(mq->sg, mq->bounce_buf, buflen); + + return 1; + } +@@ -387,19 +341,19 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq) + * If writing, bounce the data to the buffer before the request + * is sent to the host driver + */ +-void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq) ++void mmc_queue_bounce_pre(struct mmc_queue *mq) + { + unsigned long flags; + +- if (!mqrq->bounce_buf) ++ if (!mq->bounce_buf) + return; + +- if (rq_data_dir(mqrq->req) != WRITE) ++ if (rq_data_dir(mq->req) != WRITE) + return; + + local_irq_save(flags); +- sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len, +- mqrq->bounce_buf, mqrq->sg[0].length); ++ sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len, ++ mq->bounce_buf, mq->sg[0].length); + local_irq_restore(flags); + } + +@@ -407,18 +361,19 @@ void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq) + * If reading, bounce the data from the buffer after the request + * has been handled by the host driver + */ +-void mmc_queue_bounce_post(struct mmc_queue_req *mqrq) ++void mmc_queue_bounce_post(struct mmc_queue *mq) + { + unsigned long flags; + +- if (!mqrq->bounce_buf) ++ if (!mq->bounce_buf) + return; + +- if (rq_data_dir(mqrq->req) != READ) ++ if (rq_data_dir(mq->req) != READ) + return; + + local_irq_save(flags); +- sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len, +- mqrq->bounce_buf, mqrq->sg[0].length); ++ sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len, ++ mq->bounce_buf, mq->sg[0].length); + local_irq_restore(flags); + } ++ +diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h +index 0e65807..64e66e0 100644 +--- a/drivers/mmc/card/queue.h ++++ b/drivers/mmc/card/queue.h +@@ -4,33 +4,19 @@ + struct request; + struct task_struct; + +-struct mmc_blk_request { +- struct mmc_request mrq; +- struct mmc_command cmd; +- struct mmc_command stop; +- struct mmc_data data; +-}; +- +-struct mmc_queue_req { +- struct request *req; +- struct mmc_blk_request brq; +- struct scatterlist *sg; +- char *bounce_buf; +- struct scatterlist *bounce_sg; +- unsigned int bounce_sg_len; +-}; +- + struct mmc_queue { + struct mmc_card *card; + struct task_struct *thread; + struct semaphore thread_sem; + unsigned int flags; ++ struct request *req; + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; +- struct mmc_queue_req mqrq[2]; +- struct mmc_queue_req *mqrq_cur; +- struct mmc_queue_req *mqrq_prev; ++ struct scatterlist *sg; ++ char *bounce_buf; ++ struct scatterlist *bounce_sg; ++ unsigned int bounce_sg_len; + }; + + extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +@@ -38,9 +24,8 @@ extern void mmc_cleanup_queue(struct mmc_queue *); + extern void mmc_queue_suspend(struct mmc_queue *); + extern void mmc_queue_resume(struct mmc_queue *); + +-extern unsigned int mmc_queue_map_sg(struct mmc_queue *, +- struct mmc_queue_req *); +-extern void mmc_queue_bounce_pre(struct mmc_queue_req *); +-extern void mmc_queue_bounce_post(struct mmc_queue_req *); ++extern unsigned int mmc_queue_map_sg(struct mmc_queue *); ++extern void mmc_queue_bounce_pre(struct mmc_queue *); ++extern void mmc_queue_bounce_post(struct mmc_queue *); + + #endif +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index 85296df..1f453ac 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -23,8 +23,6 @@ + #include <linux/log2.h> + #include <linux/regulator/consumer.h> + #include <linux/pm_runtime.h> +-#include <linux/fault-inject.h> +-#include <linux/random.h> + + #include <linux/mmc/card.h> + #include <linux/mmc/host.h> +@@ -84,56 +82,6 @@ static void mmc_flush_scheduled_work(void) + flush_workqueue(workqueue); + } + +-#ifdef CONFIG_FAIL_MMC_REQUEST +- +-static DECLARE_FAULT_ATTR(fail_mmc_request); +- +-static int __init setup_fail_mmc_request(char *str) +-{ +- return setup_fault_attr(&fail_mmc_request, str); +-} +-__setup("fail_mmc_request=", setup_fail_mmc_request); +- +-static void mmc_should_fail_request(struct mmc_host *host, +- struct mmc_request *mrq) +-{ +- struct mmc_command *cmd = mrq->cmd; +- struct mmc_data *data = mrq->data; +- static const int data_errors[] = { +- -ETIMEDOUT, +- -EILSEQ, +- -EIO, +- }; +- +- if (!data) +- return; +- +- if (cmd->error || data->error || !host->make_it_fail || +- !should_fail(&fail_mmc_request, data->blksz * data->blocks)) +- return; +- +- data->error = data_errors[random32() % ARRAY_SIZE(data_errors)]; +- data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9; +-} +- +-static int __init fail_mmc_request_debugfs(void) +-{ +- return init_fault_attr_dentries(&fail_mmc_request, +- "fail_mmc_request"); +-} +- +-late_initcall(fail_mmc_request_debugfs); +- +-#else /* CONFIG_FAIL_MMC_REQUEST */ +- +-static inline void mmc_should_fail_request(struct mmc_host *host, +- struct mmc_data *data) +-{ +-} +- +-#endif /* CONFIG_FAIL_MMC_REQUEST */ +- +- + /** + * mmc_request_done - finish processing an MMC request + * @host: MMC host which completed request +@@ -160,8 +108,6 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) + cmd->error = 0; + host->ops->request(host, mrq); + } else { +- mmc_should_fail_request(host, mrq); +- + led_trigger_event(host->led, LED_OFF); + + pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", +@@ -252,73 +198,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) + + static void mmc_wait_done(struct mmc_request *mrq) + { +- complete(&mrq->completion); +-} +- +-/** +- * mmc_pre_req - Prepare for a new request +- * @host: MMC host to prepare command +- * @mrq: MMC request to prepare for +- * @is_first_req: true if there is no previous started request +- * that may run in parellel to this call, otherwise false +- * +- * mmc_pre_req() is called in prior to mmc_start_req() to let +- * host prepare for the new request. Preparation of a request may be +- * performed while another request is running on the host. +- */ +-void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, +- bool is_first_req) +-{ +- if (host->ops->pre_req) +- host->ops->pre_req(host, mrq, is_first_req); ++ complete(mrq->done_data); + } +-EXPORT_SYMBOL(mmc_pre_req); +- +-/** +- * mmc_post_req - Post process a completed request +- * @host: MMC host to post process command +- * @mrq: MMC request to post process for +- * @err: Error, if none zero, clean up any resources made in pre_req +- * +- * Let the host post process a completed request. Post processing of +- * a request may be performed while another reuqest is running. +- */ +-void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err) +-{ +- if (host->ops->post_req) +- host->ops->post_req(host, mrq, err); +-} +-EXPORT_SYMBOL(mmc_post_req); +- +-/** +- * mmc_start_req - start a request +- * @host: MMC host to start command +- * @mrq: MMC request to start +- * +- * Start a new MMC custom command request for a host. +- * Does not wait for the command to complete. +- */ +-void mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) +-{ +- init_completion(&mrq->completion); +- mrq->done = mmc_wait_done; +- +- mmc_start_request(host, mrq); +-} +-EXPORT_SYMBOL(mmc_start_req); +- +-/** +- * mmc_wait_for_req_done - wait for completion of request +- * @mrq: MMC request to wait for +- * +- * Wait for the command to complete. Does not attempt to parse the +- * response. +- */ +-void mmc_wait_for_req_done(struct mmc_request *mrq) +-{ +- wait_for_completion(&mrq->completion); +-} +-EXPORT_SYMBOL(mmc_wait_for_req_done); + + /** + * mmc_wait_for_req - start a request and wait for completion +@@ -331,9 +212,16 @@ EXPORT_SYMBOL(mmc_wait_for_req_done); + */ + void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) + { +- mmc_start_req(host, mrq); +- mmc_wait_for_req_done(mrq); ++ DECLARE_COMPLETION_ONSTACK(complete); ++ ++ mrq->done_data = &complete; ++ mrq->done = mmc_wait_done; ++ ++ mmc_start_request(host, mrq); ++ ++ wait_for_completion(&complete); + } ++ + EXPORT_SYMBOL(mmc_wait_for_req); + + /** +diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c +index 588e76f..998797e 100644 +--- a/drivers/mmc/core/debugfs.c ++++ b/drivers/mmc/core/debugfs.c +@@ -188,11 +188,6 @@ void mmc_add_host_debugfs(struct mmc_host *host) + root, &host->clk_delay)) + goto err_node; + #endif +-#ifdef CONFIG_FAIL_MMC_REQUEST +- if (!debugfs_create_u8("make-it-fail", S_IRUSR | S_IWUSR, +- root, &host->make_it_fail)) +- goto err_node; +-#endif + return; + + err_node: +diff --git a/drivers/mmc/host/glamo-mci.c b/drivers/mmc/host/glamo-mci.c +index 02c4b69..f298658 100644 +--- a/drivers/mmc/host/glamo-mci.c ++++ b/drivers/mmc/host/glamo-mci.c +@@ -49,10 +49,10 @@ struct glamo_mci_host { + unsigned short vdd; + char power_mode; + +- unsigned long transfer_start; +- unsigned long request_start; +- + unsigned char request_counter; ++ ++ struct workqueue_struct *workqueue; ++ struct work_struct read_work; + }; + + static void glamo_mci_send_request(struct mmc_host *mmc, +@@ -165,21 +165,12 @@ static int glamo_mci_clock_enable(struct mmc_host *mmc) + return 0; + } + +-static void __iomem *glamo_mci_get_data_addr(struct glamo_mci_host *host, +- struct mmc_data *data) +-{ +- void __iomem *addr = host->data_base; +- +- if (data->host_cookie & 1) +- addr += resource_size(host->data_mem) / 2; +- +- return addr; +-} + ++#ifndef GLAMO_MCI_WORKER + static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data) + { +- void __iomem *from_ptr = glamo_mci_get_data_addr(host, data); + struct sg_mapping_iter miter; ++ uint16_t __iomem *from_ptr = host->data_base; + + dev_dbg(&host->pdev->dev, "pio_read():\n"); + +@@ -187,7 +178,9 @@ static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data) + + while (sg_miter_next(&miter)) { + memcpy(miter.addr, from_ptr, miter.length); +- from_ptr += miter.length; ++ from_ptr += miter.length >> 1; ++ ++ data->bytes_xfered += miter.length; + } + + sg_miter_stop(&miter); +@@ -195,18 +188,19 @@ static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data) + dev_dbg(&host->pdev->dev, "pio_read(): " + "complete (no more data)\n"); + } ++#endif + + static void do_pio_write(struct glamo_mci_host *host, struct mmc_data *data) + { +- void __iomem *to_ptr = glamo_mci_get_data_addr(host, data); + struct sg_mapping_iter miter; ++ uint16_t __iomem *to_ptr = host->data_base; + + dev_dbg(&host->pdev->dev, "pio_write():\n"); + sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_FROM_SG); + + while (sg_miter_next(&miter)) { + memcpy(to_ptr, miter.addr, miter.length); +- to_ptr += miter.length; ++ to_ptr += miter.length >> 1; + + data->bytes_xfered += miter.length; + } +@@ -284,11 +278,12 @@ static irqreturn_t glamo_mci_irq(int irq, void *data) + if (mrq->stop) + glamo_mci_send_command(host, mrq->stop); + +- if (mrq->data && (mrq->data->flags & MMC_DATA_READ)) { +- mrq->data->bytes_xfered = mrq->data->blocks * mrq->data->blksz; +- if (!mrq->data->host_cookie) +- do_pio_read(host, mrq->data); +- } ++ if (cmd->data->flags & MMC_DATA_READ) ++#ifndef GLAMO_MCI_WORKER ++ do_pio_read(host, cmd->data); ++#else ++ flush_workqueue(host->workqueue); ++#endif + + if (mrq->stop) + mrq->stop->error = glamo_mci_wait_idle(host, jiffies + HZ); +@@ -300,6 +295,64 @@ done: + return IRQ_HANDLED; + } + ++#ifdef GLAMO_MCI_WORKER ++static void glamo_mci_read_worker(struct work_struct *work) ++{ ++ struct glamo_mci_host *host = container_of(work, struct glamo_mci_host, ++ read_work); ++ struct mmc_command *cmd; ++ uint16_t status; ++ uint16_t blocks_ready; ++ size_t data_read = 0; ++ size_t data_ready; ++ struct scatterlist *sg; ++ uint16_t __iomem *from_ptr = host->data_base; ++ void *sg_pointer; ++ ++ ++ cmd = host->mrq->cmd; ++ sg = cmd->data->sg; ++ do { ++ /* ++ * TODO: How to get rid of that? ++ * Maybe just drop it... In fact, it is already handled in ++ * the IRQ handler, maybe we should only check cmd->error. ++ * But the question is: what happens between the moment ++ * the error occurs, and the moment the IRQ handler handles it? ++ */ ++ status = glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1); ++ ++ if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT)) ++ cmd->error = -ETIMEDOUT; ++ if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) ++ cmd->error = -EILSEQ; ++ if (cmd->error) { ++ dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", ++ status); ++ return; ++ } ++ ++ blocks_ready = glamomci_reg_read(host, GLAMO_REG_MMC_RB_BLKCNT); ++ data_ready = blocks_ready * cmd->data->blksz; ++ ++ if (data_ready == data_read) ++ yield(); ++ ++ while (sg && data_read + sg->length <= data_ready) { ++ sg_pointer = page_address(sg_page(sg)) + sg->offset; ++ memcpy(sg_pointer, from_ptr, sg->length); ++ from_ptr += sg->length >> 1; ++ ++ data_read += sg->length; ++ ++ sg = sg_next(sg); ++ } ++ ++ } while (sg); ++ cmd->data->bytes_xfered = data_read; ++} ++#endif ++ + static void glamo_mci_send_command(struct glamo_mci_host *host, + struct mmc_command *cmd) + { +@@ -480,29 +533,28 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, + (readw(®_resp[2]) << 24); + } + } ++ ++#ifdef GLAMO_MCI_WORKER ++ /* We'll only get an interrupt when all data has been transfered. ++ By starting to copy data when it's avaiable we can increase ++ throughput by up to 30%. */ ++ if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) ++ queue_work(host->workqueue, &host->read_work); ++#endif ++ + } + + static int glamo_mci_prepare_pio(struct glamo_mci_host *host, + struct mmc_data *data) + { +- unsigned long addr = host->data_mem->start; +- +- if (data->host_cookie & 1) +- addr += resource_size(host->data_mem) / 2; +- + /* set up the block info */ + glamomci_reg_write(host, GLAMO_REG_MMC_DATBLKLEN, data->blksz); + glamomci_reg_write(host, GLAMO_REG_MMC_DATBLKCNT, data->blocks); + +- if (data->flags & MMC_DATA_WRITE) { +- glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS1, addr); +- glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS2, addr >> 16); +- } else { +- glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS1, addr); +- glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS2, addr >> 16); +- } ++ data->bytes_xfered = 0; + +- if ((data->flags & MMC_DATA_WRITE) && !data->host_cookie) ++ /* if write, prep the write into the shared RAM before the command */ ++ if (data->flags & MMC_DATA_WRITE) + do_pio_write(host, data); + + dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n", +@@ -517,8 +569,6 @@ static void glamo_mci_send_request(struct mmc_host *mmc, + struct mmc_command *cmd = mrq->cmd; + + host->request_counter++; +- host->request_start = jiffies; +- + if (cmd->data) { + if (glamo_mci_prepare_pio(host, cmd->data)) { + cmd->error = -EIO; +@@ -639,42 +689,21 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + mmc_host_lazy_disable(host->mmc); + } + +-static void glamo_mci_pre_request(struct mmc_host *mmc, +- struct mmc_request *mrq, bool is_first_req) +-{ +- struct glamo_mci_host *host = mmc_priv(mmc); +- +- mrq->data->host_cookie = (host->request_counter & 1) | 2; +- +- /* if write, prep the write into the shared RAM before the command */ +- if (mrq->data->flags & MMC_DATA_WRITE) +- do_pio_write(host, mrq->data); +-} + +-static void glamo_mci_post_request(struct mmc_host *mmc, +- struct mmc_request *mrq, int err) ++/* ++ * no physical write protect supported by us ++ */ ++static int glamo_mci_get_ro(struct mmc_host *mmc) + { +- struct glamo_mci_host *host = mmc_priv(mmc); +- +- if (!mrq->data->host_cookie) +- return; +- +- if (err) +- return; +- +- if (mrq->data->flags & MMC_DATA_READ) +- do_pio_read(host, mrq->data); +- +- mrq->data->host_cookie = 0; ++ return 0; + } + + static struct mmc_host_ops glamo_mci_ops = { + .enable = glamo_mci_clock_enable, + .disable = glamo_mci_clock_disable, + .request = glamo_mci_send_request, +- .post_req = glamo_mci_post_request, +- .pre_req = glamo_mci_pre_request, + .set_ios = glamo_mci_set_ios, ++ .get_ro = glamo_mci_get_ro, + }; + + static int __devinit glamo_mci_probe(struct platform_device *pdev) +@@ -702,6 +731,11 @@ static int __devinit glamo_mci_probe(struct platform_device *pdev) + + host->irq = platform_get_irq(pdev, 0); + ++#ifdef GLAMO_MCI_WORKER ++ INIT_WORK(&host->read_work, glamo_mci_read_worker); ++ host->workqueue = create_singlethread_workqueue("glamo-mci-read"); ++#endif ++ + host->regulator = regulator_get(pdev->dev.parent, "SD_3V3"); + if (IS_ERR(host->regulator)) { + dev_err(&pdev->dev, "Cannot proceed without regulator.\n"); +@@ -789,7 +823,7 @@ static int __devinit glamo_mci_probe(struct platform_device *pdev) + + mmc->max_blk_count = (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */ + mmc->max_blk_size = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */ +- mmc->max_req_size = resource_size(host->data_mem) / 2; ++ mmc->max_req_size = resource_size(host->data_mem); + mmc->max_seg_size = mmc->max_req_size; + mmc->max_segs = 128; + +@@ -834,6 +868,9 @@ probe_free_mem_region_mmio: + probe_regulator_put: + regulator_put(host->regulator); + probe_free_host: ++#ifdef GLAMO_MCI_WORKER ++ destroy_workqueue(host->workqueue); ++#endif + mmc_free_host(mmc); + probe_out: + return ret; +@@ -844,6 +881,11 @@ static int __devexit glamo_mci_remove(struct platform_device *pdev) + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct glamo_mci_host *host = mmc_priv(mmc); + ++#ifdef GLAMO_MCI_WORKER ++ flush_workqueue(host->workqueue); ++ destroy_workqueue(host->workqueue); ++#endif ++ + mmc_host_enable(mmc); + mmc_remove_host(mmc); + mmc_host_disable(mmc); +diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c +index cb5d2c0..66b75c6 100644 +--- a/drivers/mtd/nand/s3c2410.c ++++ b/drivers/mtd/nand/s3c2410.c +@@ -773,6 +773,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, + chip->priv = nmtd; + chip->options = set->options; + chip->controller = &info->controller; ++ chip->badblockbits = 8; + + switch (info->cpu_type) { + case TYPE_S3C2410: +diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c +index a68af2d..02bd7b0 100644 +--- a/drivers/usb/host/ohci-s3c2410.c ++++ b/drivers/usb/host/ohci-s3c2410.c +@@ -22,6 +22,10 @@ + #include <linux/platform_device.h> + #include <linux/clk.h> + #include <plat/usb-control.h> ++#include <mach/hardware.h> ++#include <mach/gpio-fns.h> ++#include <mach/regs-gpio.h> ++#include <mach/gta02.h> + + #define valid_port(idx) ((idx) == 1 || (idx) == 2) + +@@ -306,6 +310,42 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) + local_irq_restore(flags); + } + ++/* switching of USB pads */ ++static ssize_t show_usb_mode(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ if (__raw_readl(S3C24XX_MISCCR) & S3C2410_MISCCR_USBHOST) ++ return sprintf(buf, "host\n"); ++ ++ return sprintf(buf, "device\n"); ++} ++ ++static ssize_t set_usb_mode(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (!strncmp(buf, "host", 4)) { ++ printk("s3c2410: changing usb to host\n"); ++ s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, ++ S3C2410_MISCCR_USBHOST); ++ /* FIXME: ++ * - call machine-specific disable-pullup function i ++ * - enable +Vbus (if hardware supports it) ++ */ ++ s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 0); ++ } else if (!strncmp(buf, "device", 6)) { ++ printk("s3c2410: changing usb to device\n"); ++ s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, 0); ++ s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 1); ++ } else { ++ printk("s3c2410: unknown mode\n"); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++static DEVICE_ATTR(usb_mode, S_IRUGO | S_IWUSR, show_usb_mode, set_usb_mode); ++ + /* may be called without controller electrically present */ + /* may be called with controller, bus, and devices active */ + +@@ -323,6 +363,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) + static void + usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) + { ++ device_remove_file(&dev->dev, &dev_attr_usb_mode); + usb_remove_hcd(hcd); + s3c2410_stop_hc(dev); + iounmap(hcd->regs); +@@ -390,8 +431,15 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, + if (retval != 0) + goto err_ioremap; + ++ retval = device_create_file(&dev->dev, &dev_attr_usb_mode); ++ if (retval != 0) ++ goto err_hcd; ++ + return 0; + ++ err_hcd: ++ usb_remove_hcd(hcd); ++ + err_ioremap: + s3c2410_stop_hc(dev); + iounmap(hcd->regs); +diff --git a/drivers/video/glamo-fb.c b/drivers/video/glamo-fb.c +index 45f5a2b..6c3fe9c 100644 +--- a/drivers/video/glamo-fb.c ++++ b/drivers/video/glamo-fb.c +@@ -312,7 +312,7 @@ static int glamofb_cmd_mode(struct glamofb_handle *gfb, int on) + glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1, + GLAMO_LCD_CMD_TYPE_DISP | + GLAMO_LCD_CMD_DATA_DISP_SYNC); +- ++ mdelay(1); + glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1, + GLAMO_LCD_CMD_TYPE_DISP | + GLAMO_LCD_CMD_DATA_DISP_FIRE); +diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h +new file mode 100644 +index 0000000..01c4ac9 +--- /dev/null ++++ b/include/linux/lis302dl.h +@@ -0,0 +1,155 @@ ++#ifndef _LINUX_LIS302DL_H ++#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; ++ 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 { ++ struct lis302dl_platform_data *pdata; ++ struct device *dev; ++ struct input_dev *input_dev; ++ unsigned int flags; ++ unsigned int threshold; ++ unsigned int duration; ++ u32 overruns; ++ struct { ++ unsigned int threshold; /* mg */ ++ unsigned int duration; /* ms */ ++ } wakeup; ++ 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_ctrl2 { ++ LIS302DL_CTRL2_HPC1 = 0x01, ++ LIS302DL_CTRL2_HPC2 = 0x02, ++ LIS302DL_CTRL2_HPFF1 = 0x04, ++ LIS302DL_CTRL2_HPFF2 = 0x08, ++ LIS302DL_CTRL2_FDS = 0x10, ++ LIS302DL_CTRL2_BOOT = 0x40, ++ LIS302DL_CTRL2_SIM = 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, ++}; ++ ++/* Wakeup/freefall interrupt defs */ ++enum lis302dl_reg_ffwucfg { ++ LIS302DL_FFWUCFG_XLIE = 0x01, ++ LIS302DL_FFWUCFG_XHIE = 0x02, ++ LIS302DL_FFWUCFG_YLIE = 0x04, ++ LIS302DL_FFWUCFG_YHIE = 0x08, ++ LIS302DL_FFWUCFG_ZLIE = 0x10, ++ LIS302DL_FFWUCFG_ZHIE = 0x20, ++ LIS302DL_FFWUCFG_LIR = 0x40, ++ LIS302DL_FFWUCFG_AOI = 0x80, ++}; ++ ++enum lis302dl_reg_ffwuths { ++ LIS302DL_FFWUTHS_DCRM = 0x80, ++}; ++ ++enum lis302dl_reg_ffwusrc { ++ LIS302DL_FFWUSRC_XL = 0x01, ++ LIS302DL_FFWUSRC_XH = 0x02, ++ LIS302DL_FFWUSRC_YL = 0x04, ++ LIS302DL_FFWUSRC_YH = 0x08, ++ LIS302DL_FFWUSRC_ZL = 0x10, ++ LIS302DL_FFWUSRC_ZH = 0x20, ++ LIS302DL_FFWUSRC_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_1 0x0001 /* wake up from free fall */ ++#define LIS302DL_F_WUP_FF_2 0x0002 ++#define LIS302DL_F_WUP_FF 0x0003 ++#define LIS302DL_F_WUP_CLICK 0x0004 ++#define LIS302DL_F_POWER 0x0010 ++#define LIS302DL_F_FS 0x0020 /* ADC full scale */ ++#define LIS302DL_F_INPUT_OPEN 0x0040 /* Set if input device is opened */ ++#define LIS302DL_F_IRQ_WAKE 0x0080 /* IRQ is setup in wake mode */ ++#define LIS302DL_F_DR 0x0100 /* Data rate, 400Hz/100Hz */ ++ ++ ++#endif /* _LINUX_LIS302DL_H */ ++ +diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h +index 5bbfb71..07f27af 100644 +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -117,7 +117,6 @@ struct mmc_data { + + unsigned int sg_len; /* size of scatter list */ + struct scatterlist *sg; /* I/O scatter list */ +- s32 host_cookie; /* host private data */ + }; + + struct mmc_request { +@@ -125,19 +124,13 @@ struct mmc_request { + struct mmc_data *data; + struct mmc_command *stop; + +- struct completion completion; ++ void *done_data; /* completion data */ + void (*done)(struct mmc_request *);/* completion function */ + }; + + struct mmc_host; + struct mmc_card; + +-extern void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, +- bool is_first_req); +-extern void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, +- int err); +-extern void mmc_start_req(struct mmc_host *host, struct mmc_request *mrq); +-extern void mmc_wait_for_req_done(struct mmc_request *mrq); + extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); + extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); + extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 8b2b44b..bcb793e 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -88,15 +88,6 @@ struct mmc_host_ops { + */ + int (*enable)(struct mmc_host *host); + int (*disable)(struct mmc_host *host, int lazy); +- /* +- * It is optional for the host to implement pre_req and post_req in +- * order to support double buffering of requests (prepare one +- * request while another request is active). +- */ +- void (*post_req)(struct mmc_host *host, struct mmc_request *req, +- int err); +- void (*pre_req)(struct mmc_host *host, struct mmc_request *req, +- bool is_first_req); + void (*request)(struct mmc_host *host, struct mmc_request *req); + /* + * Avoid calling these three functions too often or in a "fast path", +@@ -251,9 +242,7 @@ struct mmc_host { + #endif + + struct dentry *debugfs_root; +-#ifdef CONFIG_FAIL_MMC_REQUEST +- u8 make_it_fail; +-#endif ++ + unsigned long private[0] ____cacheline_aligned; + }; + +diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug +index 330fc70..c768bcd 100644 +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -1057,17 +1057,6 @@ config FAIL_IO_TIMEOUT + Only works with drivers that use the generic timeout handling, + for others it wont do anything. + +-config FAIL_MMC_REQUEST +- bool "Fault-injection capability for MMC IO" +- select DEBUG_FS +- depends on FAULT_INJECTION +- help +- Provide fault-injection capability for MMC IO. +- This will make the mmc core return data errors. This is +- useful for testing the error handling in the mmc block device +- and how the mmc host driver handle retries from +- the block device. +- + config FAULT_INJECTION_DEBUG_FS + bool "Debugfs entries for fault-injection capabilities" + depends on FAULT_INJECTION && SYSFS && DEBUG_FS +diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c +index 4f845fe..a4700c0 100644 +--- a/sound/soc/samsung/neo1973_wm8753.c ++++ b/sound/soc/samsung/neo1973_wm8753.c +@@ -19,6 +19,7 @@ + #include <linux/gpio.h> + + #include <sound/soc.h> ++#include <sound/jack.h> + + #include <asm/mach-types.h> + #include <plat/regs-iis.h> +@@ -204,6 +205,27 @@ static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Handset Mic", NULL), + }; + ++static struct snd_soc_jack_pin hs_jack_pins[] = { ++ { ++ .pin = "Headset Mic", ++ .mask = SND_JACK_MICROPHONE, ++ }, ++ { ++ .pin = "Stereo Out", ++ .mask = SND_JACK_HEADPHONE, ++ .invert = 1, ++ }, ++}; ++ ++static struct snd_soc_jack_gpio hs_jack_gpios[] = { ++ { ++ .gpio = GTA02_GPIO_JACK_INSERT, ++ .name = "headset-gpio", ++ .report = SND_JACK_HEADSET, ++ .debounce_time = 100, ++ }, ++}; ++ + static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { + /* Connections to the GSM Module */ + {"GSM Line Out", NULL, "MONO1"}, +@@ -377,6 +399,8 @@ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) + + /* GTA01 specific controls */ + ++static struct snd_soc_jack hs_jack; ++ + #ifdef CONFIG_MACH_NEO1973_GTA01 + + static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { +@@ -512,6 +536,24 @@ static int __init neo1973_init(void) + if (ret) + goto err_put_device; + ++ err = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, &hs_jack); ++ if (err) { ++ dev_err(codec->card->dev, "failed to alloc headset jack\n"); ++ return err; ++ } ++ ++ err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), hs_jack_pins); ++ if (err) { ++ dev_err(codec->card->dev, "failed to add headset jack pins\n"); ++ return err; ++ } ++ ++ err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); ++ if (err) { ++ dev_err(codec->card->dev, "failed to add headset jack gpios\n"); ++ return err; ++ } ++ + return 0; + + err_put_device: +@@ -527,6 +569,7 @@ module_init(neo1973_init); + + static void __exit neo1973_exit(void) + { ++ snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); + platform_device_unregister(neo1973_snd_device); + + if (machine_is_neo1973_gta02()) { |