aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormerge <null@invalid>2008-11-27 08:37:56 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-27 08:37:56 +0000
commita560d8579b4b009f847972667b7597c89030e26c (patch)
tree31dc5e17d93057b2e9290720a8b1f453201dead2
parente21ce7019cc3b9495be8678ef2c41569cc03021e (diff)
MERGE-via-balaji-tracking-hist-MERGE-via-stable-tracking-hist-config-gta02-uplevel-patch
balaji-tracking-hist top was MERGE-via-stable-tracking-hist-config-gta02-uplevel-patch / eb381acecca375d0a7b88cfe640504a8a1fa4c39 ... parent commitmessage: From: merge <null@invalid> MERGE-via-stable-tracking-hist-config-gta02-uplevel-patch stable-tracking-hist top was config-gta02-uplevel-patch / 0e07e39074bbdb938cfefaea6ad7823282cc914c ... parent commitmessage: From: Andy Green <andy@openmoko.com> config-gta02-uplevel.patch Signed-off-by: Andy Green <andy@openmoko.com>
-rw-r--r--arch/arm/configs/gta02-moredrivers-defconfig7
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig6
-rw-r--r--arch/arm/plat-s3c24xx/neo1973_pm_bt.c59
-rw-r--r--drivers/android/binder.c4
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c248
-rw-r--r--drivers/mmc/core/core.c3
-rw-r--r--drivers/mmc/host/s3cmci.c48
-rw-r--r--drivers/mmc/host/s3cmci.h10
-rw-r--r--include/linux/mmc/core.h2
-rw-r--r--init/Kconfig9
10 files changed, 311 insertions, 85 deletions
diff --git a/arch/arm/configs/gta02-moredrivers-defconfig b/arch/arm/configs/gta02-moredrivers-defconfig
index ef68a69eee9..823a3da8339 100644
--- a/arch/arm/configs/gta02-moredrivers-defconfig
+++ b/arch/arm/configs/gta02-moredrivers-defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.28-rc4
-# Thu Nov 20 09:10:06 2008
+# Thu Nov 27 08:12:15 2008
#
CONFIG_ARM=y
CONFIG_HAVE_PWM=y
@@ -628,7 +628,9 @@ CONFIG_WIRELESS_EXT=y
CONFIG_WIRELESS_EXT_SYSFS=y
# CONFIG_MAC80211 is not set
# CONFIG_IEEE80211 is not set
-# CONFIG_RFKILL is not set
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_RFKILL_LEDS=y
# CONFIG_NET_9P is not set
#
@@ -875,6 +877,7 @@ CONFIG_USB_ARMLINUX=y
CONFIG_USB_EPSON2888=y
CONFIG_USB_KC2190=y
CONFIG_USB_NET_ZAURUS=m
+# CONFIG_USB_HSO is not set
# CONFIG_WAN is not set
CONFIG_PPP=m
CONFIG_PPP_MULTILINK=y
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index 54b0ec4fa62..1bd3141a1d8 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -83,4 +83,10 @@ config MACH_SMDK
help
Common machine code for SMDK2410 and SMDK2440
+config MACH_NEO1973
+ bool
+ select RFKILL
+ help
+ Common machine code for Neo1973 hardware
+
endif
diff --git a/arch/arm/plat-s3c24xx/neo1973_pm_bt.c b/arch/arm/plat-s3c24xx/neo1973_pm_bt.c
index d3a046cada1..336c61e443c 100644
--- a/arch/arm/plat-s3c24xx/neo1973_pm_bt.c
+++ b/arch/arm/plat-s3c24xx/neo1973_pm_bt.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/rfkill.h>
#include <linux/err.h>
#include <mach/hardware.h>
@@ -70,6 +71,41 @@ static ssize_t bt_read(struct device *dev, struct device_attribute *attr,
}
}
+static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
+{
+ struct device *dev = data;
+ unsigned long on = (state == RFKILL_STATE_ON);
+ unsigned int vol;
+
+ if (machine_is_neo1973_gta01()) {
+ /* if we are powering up, assert reset, then power,
+ * then release reset */
+ if (on) {
+ neo1973_gpb_setpin(GTA01_GPIO_BT_EN, 0);
+ pcf50606_voltage_set(pcf50606_global,
+ PCF50606_REGULATOR_D1REG,
+ 3100);
+ }
+ pcf50606_onoff_set(pcf50606_global,
+ PCF50606_REGULATOR_D1REG, on);
+ neo1973_gpb_setpin(GTA01_GPIO_BT_EN, on);
+ } else if (machine_is_neo1973_gta02()) {
+ if (s3c2410_gpio_getpin(GTA02_GPIO_BT_EN) == on)
+ return 0;
+ neo1973_gpb_setpin(GTA02_GPIO_BT_EN, !on);
+ pcf50633_voltage_set(pcf50633_global,
+ PCF50633_REGULATOR_LDO4, on ? 3200 : 0);
+ pcf50633_onoff_set(pcf50633_global,
+ PCF50633_REGULATOR_LDO4, on);
+ vol = pcf50633_voltage_get(pcf50633_global,
+ PCF50633_REGULATOR_LDO4);
+ dev_info(dev, "GTA02 Set PCF50633 LDO4 = %d\n", vol);
+ neo1973_gpb_setpin(GTA02_GPIO_BT_EN, on);
+ }
+
+ return 0;
+}
+
static ssize_t bt_write(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -79,6 +115,11 @@ static ssize_t bt_write(struct device *dev, struct device_attribute *attr,
struct regulator *regulator;
if (!strcmp(attr->attr.name, "power_on")) {
+ struct rfkill *rfkill = dev_get_drvdata(dev);
+ enum rfkill_state state = on ? RFKILL_STATE_ON : RFKILL_STATE_OFF;
+ bt_rfkill_toggle_radio(dev, state);
+ rfkill->state = state;
+
if (machine_is_neo1973_gta01()) {
/* if we are powering up, assert reset, then power,
* then release reset */
@@ -164,6 +205,7 @@ static struct attribute_group gta01_bt_attr_group = {
static int __init gta01_bt_probe(struct platform_device *pdev)
{
+ struct rfkill *rfkill;
struct regulator *regulator;
struct gta01_pm_bt_data *bt_data;
@@ -194,16 +236,32 @@ static int __init gta01_bt_probe(struct platform_device *pdev)
neo1973_gpb_setpin(GTA02_GPIO_BT_EN, 0);
}
+ rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH);
+
+ rfkill->name = pdev->name;
+ rfkill->data = pdev;
+ rfkill->state = -1;
+ rfkill->toggle_radio = bt_rfkill_toggle_radio;
+
+ rfkill_register(rfkill);
+
+ platform_set_drvdata(pdev, rfkill);
+
return sysfs_create_group(&pdev->dev.kobj, &gta01_bt_attr_group);
}
static int gta01_bt_remove(struct platform_device *pdev)
{
+ struct rfkill *rfkill = platform_get_drvdata(pdev);
+
struct gta01_pm_bt_data *bt_data;
struct regulator *regulator;
sysfs_remove_group(&pdev->dev.kobj, &gta01_bt_attr_group);
+ rfkill_unregister(rfkill);
+ rfkill_free(rfkill);
+
bt_data = dev_get_drvdata(&pdev->dev);
if (!bt_data || !bt_data->regulator)
return 0;
@@ -216,6 +274,7 @@ static int gta01_bt_remove(struct platform_device *pdev)
regulator_put(regulator);
+>>>>>>> patched
return 0;
}
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index a6a8380c6d1..80a96bef392 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -54,11 +54,7 @@ static int binder_read_proc_proc(
#define SZ_4M 0x400000
#endif
-#ifndef __i386__
-#define FORBIDDEN_MMAP_FLAGS (VM_WRITE | VM_EXEC)
-#else
#define FORBIDDEN_MMAP_FLAGS (VM_WRITE)
-#endif
#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64)
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index 253dc5b7eac..990408886fe 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -49,6 +49,8 @@
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -89,13 +91,16 @@ MODULE_LICENSE("GPL");
* Definitions & global arrays.
*/
-#define TOUCH_STANDBY_FLAG 0
-#define TOUCH_PRESSED_FLAG 1
-#define TOUCH_RELEASE_FLAG 2
+static char *s3c2410ts_name = "s3c2410 TouchScreen";
-#define TOUCH_RELEASE_TIMEOUT (HZ >> 4)
+#define TS_RELEASE_TIMEOUT (HZ >> 4) /* ~ 60 milliseconds */
+#define TS_EVENT_FIFO_SIZE (2 << 6) /* must be a power of 2 */
-static char *s3c2410ts_name = "s3c2410 TouchScreen";
+#define TS_STATE_STANDBY 0 /* initial state */
+#define TS_STATE_PRESSED_PENDING 1
+#define TS_STATE_PRESSED 2
+#define TS_STATE_RELEASE_PENDING 3
+#define TS_STATE_RELEASE 4
/*
* Per-touchscreen data.
@@ -106,7 +111,8 @@ struct s3c2410ts {
struct ts_filter *tsf[MAX_TS_FILTER_CHAIN];
int coords[2]; /* just X and Y for us */
int is_down;
- int need_to_send_first_touch;
+ int state;
+ struct kfifo *event_fifo;
};
static struct s3c2410ts ts;
@@ -122,74 +128,151 @@ static inline void s3c2410_ts_connect(void)
s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}
+static void s3c2410_ts_start_adc_conversion(void)
+{
+ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+ base_addr + S3C2410_ADCTSC);
+ writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,
+ base_addr + S3C2410_ADCCON);
+}
+
+/*
+ * Just send the input events.
+ */
+
enum ts_input_event {IE_DOWN = 0, IE_UP, IE_UPDATE};
-static void ts_input_report(int event)
+static void ts_input_report(int event, int coords[])
{
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+ static char *s[] = {"down", "up", "update"};
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+#endif
+
if (event == IE_DOWN || event == IE_UPDATE) {
- input_report_abs(ts.dev, ABS_X, ts.coords[0]);
- input_report_abs(ts.dev, ABS_Y, ts.coords[1]);
+ input_report_abs(ts.dev, ABS_X, coords[0]);
+ input_report_abs(ts.dev, ABS_Y, coords[1]);
input_report_key(ts.dev, BTN_TOUCH, 1);
input_report_abs(ts.dev, ABS_PRESSURE, 1);
+
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+ printk(DEBUG_LVL "T:%06d %6s (X:%03d, Y:%03d)\n",
+ (int)tv.tv_usec, s[event], coords[0], coords[1]);
+#endif
} else {
input_report_key(ts.dev, BTN_TOUCH, 0);
input_report_abs(ts.dev, ABS_PRESSURE, 0);
- }
-
- input_sync(ts.dev);
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
- {
- static char *s[] = {"down", "up", "update"};
- struct timeval tv;
- do_gettimeofday(&tv);
- printk(DEBUG_LVL "T:%06d %6s (X:%03d, Y:%03d)\n",
- (int)tv.tv_usec, s[event], ts.coords[0], ts.coords[1]);
- }
+ printk(DEBUG_LVL "T:%06d %6s\n",
+ (int)tv.tv_usec, s[event]);
#endif
+ }
+
+ input_sync(ts.dev);
}
-static void touch_timer_fire(unsigned long data);
-static struct timer_list touch_timer =
- TIMER_INITIALIZER(touch_timer_fire, 0, 0);
+/*
+ * Manage state of the touchscreen and send events.
+ */
+
+
+static void event_send_timer_f(unsigned long data);
+
+static struct timer_list event_send_timer =
+ TIMER_INITIALIZER(event_send_timer_f, 0, 0);
-static void touch_timer_fire(unsigned long data)
+static void event_send_timer_f(unsigned long data)
{
- if (ts.tsf[0])
- (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]);
+ static unsigned long running;
+ static int noop_counter;
+ int event_type;
+
+ if (unlikely(test_and_set_bit(0, &running))) {
+ mod_timer(&event_send_timer,
+ jiffies + TS_RELEASE_TIMEOUT);
+ return;
+ }
+
+ while (__kfifo_get(ts.event_fifo, (unsigned char *)&event_type,
+ sizeof(int))) {
+ int buf[2];
+
+ switch (event_type) {
+ case 'D':
+ if (ts.state == TS_STATE_RELEASE_PENDING)
+ /* Ignore short UP event */
+ ts.state = TS_STATE_PRESSED;
+ else
+ /* Defer PRESSED until we get a valid point */
+ ts.state = TS_STATE_PRESSED_PENDING;
+ break;
+
+ case 'U':
+ ts.state = TS_STATE_RELEASE_PENDING;
+ break;
+
+ case 'P':
+ if (ts.is_down) /* stylus_action needs a conversion */
+ s3c2410_ts_start_adc_conversion();
+
+ if (unlikely(__kfifo_get(ts.event_fifo,
+ (unsigned char *)buf,
+ sizeof(int) * 2)
+ != sizeof(int) * 2))
+ goto ts_exit_error;
+
+ if (ts.state == TS_STATE_PRESSED_PENDING)
+ ts_input_report(IE_DOWN, buf);
+ else
+ ts_input_report(IE_UPDATE, buf);
+
+ ts.state = TS_STATE_PRESSED;
- if (ts.is_down && ts.need_to_send_first_touch == TOUCH_RELEASE_FLAG)
- ts.need_to_send_first_touch = TOUCH_PRESSED_FLAG;
-
- if ( ts.is_down ) {
- if ( ts.need_to_send_first_touch == TOUCH_STANDBY_FLAG )
- ts_input_report(IE_DOWN);
- else
- ts_input_report(IE_UPDATE);
- ts.need_to_send_first_touch = TOUCH_PRESSED_FLAG;
- } else if (ts.need_to_send_first_touch == TOUCH_RELEASE_FLAG)
- ts_input_report(IE_UP);
- else {
- ts.need_to_send_first_touch = TOUCH_RELEASE_FLAG;
- mod_timer(&touch_timer, jiffies + TOUCH_RELEASE_TIMEOUT);
- }
-
- if (ts.is_down) {
- writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
- base_addr+S3C2410_ADCTSC);
- writel(readl(base_addr+S3C2410_ADCCON) |
- S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+ break;
+
+ default:
+ goto ts_exit_error;
+ }
+
+ noop_counter = 0;
+ }
+
+ if (noop_counter++ >= 1) {
+ noop_counter = 0;
+ if (ts.state == TS_STATE_RELEASE_PENDING) {
+ /* We delay the UP event for a
+ * while to avoid jitter. If we get a DOWN
+ * event we do not send it. */
+ ts_input_report(IE_UP, NULL);
+ ts.state = TS_STATE_STANDBY;
+
+ if (ts.tsf[0])
+ (ts.tsf[0]->api->clear)(ts.tsf[0]);
+ }
} else {
- if (ts.tsf[0])
- (ts.tsf[0]->api->clear)(ts.tsf[0]);
- writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+ mod_timer(&event_send_timer, jiffies + TS_RELEASE_TIMEOUT);
}
+
+ clear_bit(0, &running);
+
+ return;
+
+ts_exit_error: /* should not happen unless we have a bug */
+ printk(KERN_ERR __FILE__ ": event_send_timer_f failed\n");
}
+/*
+ * Manage interrupts.
+ */
+
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
+ int event_type;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
@@ -197,18 +280,26 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
(!(data1 & S3C2410_ADCDAT0_UPDOWN));
- if (ts.is_down) {
- writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
- base_addr+S3C2410_ADCTSC);
- writel(readl(base_addr+S3C2410_ADCCON) |
- S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
- }
+ event_type = ts.is_down ? 'D' : 'U';
+
+ if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)&event_type,
+ sizeof(int)) != sizeof(int))) /* should not happen */
+ printk(KERN_ERR __FILE__": stylus_updown lost event!\n");
+
+ if (ts.is_down)
+ s3c2410_ts_start_adc_conversion();
+ else
+ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+
+ mod_timer(&event_send_timer, jiffies + 1);
return IRQ_HANDLED;
}
static irqreturn_t stylus_action(int irq, void *dev_id)
{
+ int buf[3];
+
/* grab the ADC results */
ts.coords[0] = readl(base_addr + S3C2410_ADCDAT0) &
S3C2410_ADCDAT0_XPDATA_MASK;
@@ -226,15 +317,27 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
* no real sample came out of processing yet,
* get another raw result to feed it
*/
- writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
- base_addr + S3C2410_ADCTSC);
- writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,
- base_addr + S3C2410_ADCCON);
+
+ s3c2410_ts_start_adc_conversion();
+
return IRQ_HANDLED;
real_sample:
- mod_timer(&touch_timer, jiffies + 1);
+
+ if (ts.tsf[0])
+ (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]);
+
+ buf[0] = 'P';
+ buf[1] = ts.coords[0];
+ buf[2] = ts.coords[1];
+
+ if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf,
+ sizeof(int) * 3) != sizeof(int) * 3))
+ /* should not happen */
+ printk(KERN_ERR __FILE__": stylus_action lost event!\n");
+
writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
+ mod_timer(&event_send_timer, jiffies + 1);
return IRQ_HANDLED;
}
@@ -325,12 +428,12 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
ts.dev->id.vendor = 0xDEAD;
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
- ts.need_to_send_first_touch = TOUCH_STANDBY_FLAG;
+ ts.state = TS_STATE_STANDBY;
/* create the filter chain set up for the 2 coordinates we produce */
ret = ts_filter_create_chain(
(struct ts_filter_api **)&info->filter_sequence,
- &info->filter_config, ts.tsf, ARRAY_SIZE(ts.coords));
+ (void *)&info->filter_config, ts.tsf, ARRAY_SIZE(ts.coords));
if (ret)
dev_info(&pdev->dev, "%d filter(s) initialized\n", ret);
else /* this is OK, just means there won't be any filtering */
@@ -358,22 +461,31 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
goto bail4;
}
+ ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL);
+
+ if (IS_ERR(ts.event_fifo)) {
+ ret = -EIO;
+ goto bail5;
+ }
+
dev_info(&pdev->dev, "successfully loaded\n");
/* All went ok, so register to the input system */
rc = input_register_device(ts.dev);
if (rc) {
- free_irq(IRQ_TC, ts.dev);
- free_irq(IRQ_ADC, ts.dev);
- clk_disable(adc_clock);
- iounmap(base_addr);
ret = -EIO;
- goto bail5;
+ goto bail6;
}
return 0;
+bail6:
+ kfifo_free(ts.event_fifo);
bail5:
+ free_irq(IRQ_TC, ts.dev);
+ free_irq(IRQ_ADC, ts.dev);
+ clk_disable(adc_clock);
+ iounmap(base_addr);
disable_irq(IRQ_TC);
bail4:
disable_irq(IRQ_ADC);
@@ -406,6 +518,8 @@ static int s3c2410ts_remove(struct platform_device *pdev)
ts_filter_destroy_chain(ts.tsf);
+ kfifo_free(ts.event_fifo);
+
return 0;
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index f7284b905eb..b76667e36e8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -57,10 +57,11 @@ static int mmc_schedule_delayed_work(struct delayed_work *work,
/*
* Internal function. Flush all scheduled work from the MMC work queue.
*/
-static void mmc_flush_scheduled_work(void)
+void mmc_flush_scheduled_work(void)
{
flush_workqueue(workqueue);
}
+EXPORT_SYMBOL_GPL(mmc_flush_scheduled_work);
/**
* mmc_request_done - finish processing an MMC request
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index a2bb2129bfd..45c5192869b 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -57,6 +57,7 @@ static const int dbgmap_info = dbg_info | dbg_conf;
static const int dbgmap_debug = dbg_err | dbg_debug;
static int f_max = -1; /* override maximum frequency limit */
+static int persist; /* keep interface alive across suspend/resume */
#define dbg(host, channels, args...) \
do { \
@@ -1518,18 +1519,60 @@ static int __devinit s3cmci_2440_probe(struct platform_device *dev)
#ifdef CONFIG_PM
+static int save_regs(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ unsigned from;
+ u32 *to = host->saved;
+
+ mmc_flush_scheduled_work();
+
+ local_irq_save(flags);
+ for (from = S3C2410_SDICON; from != S3C2410_SDIIMSK+4; from += 4)
+ if (from != host->sdidata)
+ *to++ = readl(host->base + from);
+ BUG_ON(to-host->saved != ARRAY_SIZE(host->saved));
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int restore_regs(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ unsigned to;
+ u32 *from = host->saved;
+
+ /*
+ * Before we begin with the necromancy, make sure we don't
+ * inadvertently start something we'll regret microseconds later.
+ */
+ from[S3C2410_SDICMDCON - S3C2410_SDICON] = 0;
+
+ local_irq_save(flags);
+ for (to = S3C2410_SDICON; to != S3C2410_SDIIMSK+4; to += 4)
+ if (to != host->sdidata)
+ writel(*from++, host->base + to);
+ BUG_ON(from-host->saved != ARRAY_SIZE(host->saved));
+ local_irq_restore(flags);
+
+ return 0;
+}
+
static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
- return mmc_suspend_host(mmc, state);
+ return persist ? save_regs(mmc) : mmc_suspend_host(mmc, state);
}
static int s3cmci_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
- return mmc_resume_host(mmc);
+ return persist ? restore_regs(mmc) : mmc_resume_host(mmc);
}
#else /* CONFIG_PM */
@@ -1588,6 +1631,7 @@ module_init(s3cmci_init);
module_exit(s3cmci_exit);
module_param(f_max, int, 0644);
+module_param(persist, int, 0644);
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index ca1ba3d58cf..c2b65b3488b 100644
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -8,6 +8,9 @@
* published by the Free Software Foundation.
*/
+
+#include <mach/regs-sdi.h>
+
/* FIXME: DMA Resource management ?! */
#define S3CMCI_DMA 0
@@ -68,6 +71,13 @@ struct s3cmci_host {
unsigned int ccnt, dcnt;
struct tasklet_struct pio_tasklet;
+ /*
+ * Here's where we save the registers during suspend. Note that we skip
+ * SDIDATA, which is at different positions on 2410 and 2440, so
+ * there's no "+1" in the array size.
+ */
+ u32 saved[(S3C2410_SDIIMSK-S3C2410_SDICON)/4];
+
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 143cebf0586..6918f9e4e7d 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -129,6 +129,8 @@ struct mmc_request {
struct mmc_host;
struct mmc_card;
+extern void mmc_flush_scheduled_work(void);
+
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/init/Kconfig b/init/Kconfig
index 4fe525fac89..e0db6566952 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -741,15 +741,6 @@ config ASHMEM
POSIX SHM but with different behavior and sporting a simpler
file-based API.
-config ASHMEM
- bool "Enable Android's Shared Memory Subsystem"
- default n
- depends on SHMEM || TINY_SHMEM
- help
- The ashmem subsystem is a new shared memory allocator, similar to
- POSIX SHM but with different behavior and sporting a simpler
- file-based API.
-
config VM_EVENT_COUNTERS
default y
bool "Enable VM event counters for /proc/vmstat" if EMBEDDED