diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.24/1265-gta01-pcf50606-disable-irq-from-suspend-until-resume.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.24/1265-gta01-pcf50606-disable-irq-from-suspend-until-resume.patch | 287 |
1 files changed, 0 insertions, 287 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.24/1265-gta01-pcf50606-disable-irq-from-suspend-until-resume.patch b/target/linux/s3c24xx/patches-2.6.24/1265-gta01-pcf50606-disable-irq-from-suspend-until-resume.patch deleted file mode 100644 index 4a34114611..0000000000 --- a/target/linux/s3c24xx/patches-2.6.24/1265-gta01-pcf50606-disable-irq-from-suspend-until-resume.patch +++ /dev/null @@ -1,287 +0,0 @@ -From 0c136b451381e760c056a7683b7fc28901f54da4 Mon Sep 17 00:00:00 2001 -From: Mike Westerhof <mwester@dls.net> -Date: Fri, 8 Aug 2008 13:10:59 +0100 -Subject: [PATCH] gta01-pcf50606-disable-irq-from-suspend-until-resume.patch - - This patch is the pcf50606 equivalent of the pcf50633 patch that - disables interrupts from the chip until after resume is complete. - In order to ensure no data is lost, the work function is called - post-resume to process any pending interrupts. - - Most of the code was quite literally re-used from Andy Green's - original patch. - -Signed-off-by: Mike Westerhof <mwester@dls.net> ---- - drivers/i2c/chips/pcf50606.c | 148 ++++++++++++++++++++++++++++++++++++++++-- - 1 files changed, 142 insertions(+), 6 deletions(-) - -diff --git a/drivers/i2c/chips/pcf50606.c b/drivers/i2c/chips/pcf50606.c -index ddba1c7..18263fb 100644 ---- a/drivers/i2c/chips/pcf50606.c -+++ b/drivers/i2c/chips/pcf50606.c -@@ -38,6 +38,7 @@ - #include <linux/interrupt.h> - #include <linux/irq.h> - #include <linux/workqueue.h> -+#include <linux/delay.h> - #include <linux/rtc.h> - #include <linux/bcd.h> - #include <linux/watchdog.h> -@@ -95,6 +96,15 @@ enum close_state { - CLOSE_STATE_ALLOW = 0x2342, - }; - -+enum pcf50606_suspend_states { -+ PCF50606_SS_RUNNING, -+ PCF50606_SS_STARTING_SUSPEND, -+ PCF50606_SS_COMPLETED_SUSPEND, -+ PCF50606_SS_RESUMING_BUT_NOT_US_YET, -+ PCF50606_SS_STARTING_RESUME, -+ PCF50606_SS_COMPLETED_RESUME, -+}; -+ - struct pcf50606_data { - struct i2c_client client; - struct pcf50606_platform_data *pdata; -@@ -110,6 +120,7 @@ struct pcf50606_data { - int onkey_seconds; - int irq; - int coldplug_done; -+ enum pcf50606_suspend_states suspend_state; - #ifdef CONFIG_PM - struct { - u_int8_t dcdc1, dcdc2; -@@ -160,6 +171,10 @@ static const u_int16_t ntc_table_10k_3370B[] = { - static inline int __reg_write(struct pcf50606_data *pcf, u_int8_t reg, - u_int8_t val) - { -+ if (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND) { -+ dev_err(&pcf->client.dev, "__reg_write while suspended.\n"); -+ dump_stack(); -+ } - return i2c_smbus_write_byte_data(&pcf->client, reg, val); - } - -@@ -178,6 +193,10 @@ static inline int32_t __reg_read(struct pcf50606_data *pcf, u_int8_t reg) - { - int32_t ret; - -+ if (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND) { -+ dev_err(&pcf->client.dev, "__reg_read while suspended.\n"); -+ dump_stack(); -+ } - ret = i2c_smbus_read_byte_data(&pcf->client, reg); - - return ret; -@@ -569,6 +588,48 @@ static void pcf50606_work(struct work_struct *work) - - mutex_lock(&pcf->working_lock); - pcf->working = 1; -+ -+ /* sanity */ -+ if (!&pcf->client.dev) -+ goto bail; -+ -+ /* -+ * if we are presently suspending, we are not in a position to deal -+ * with pcf50606 interrupts at all. -+ * -+ * Because we didn't clear the int pending registers, there will be -+ * no edge / interrupt waiting for us when we wake. But it is OK -+ * because at the end of our resume, we call this workqueue function -+ * gratuitously, clearing the pending register and re-enabling -+ * servicing this interrupt. -+ */ -+ -+ if ((pcf->suspend_state == PCF50606_SS_STARTING_SUSPEND) || -+ (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND)) -+ goto bail; -+ -+ /* -+ * If we are inside suspend -> resume completion time we don't attempt -+ * service until we have fully resumed. Although we could talk to the -+ * device as soon as I2C is up, the regs in the device which we might -+ * choose to modify as part of the service action have not been -+ * reloaded with their pre-suspend states yet. Therefore we will -+ * defer our service if we are called like that until our resume has -+ * completed. -+ * -+ * This shouldn't happen any more because we disable servicing this -+ * interrupt in suspend and don't re-enable it until resume is -+ * completed. -+ */ -+ -+ if (pcf->suspend_state && -+ (pcf->suspend_state != PCF50606_SS_COMPLETED_RESUME)) -+ goto reschedule; -+ -+ /* this is the case early in resume! Sanity check! */ -+ if (i2c_get_clientdata(&pcf->client) == NULL) -+ goto reschedule; -+ - /* - * p35 pcf50606 datasheet rev 2.2: - * ''The system controller shall read all interrupt registers in -@@ -576,10 +637,27 @@ static void pcf50606_work(struct work_struct *work) - * because if you don't INT# gets stuck asserted forever after a - * while - */ -- ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50606_REG_INT1, 3, -- pcfirq); -- if (ret != 3) -+ ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50606_REG_INT1, -+ sizeof(pcfirq), pcfirq); -+ if (ret != sizeof(pcfirq)) { - DEBUGPC("Oh crap PMU IRQ register read failed %d\n", ret); -+ /* -+ * it shouldn't fail, we no longer attempt to use -+ * I2C while it can be suspended. But we don't have -+ * much option but to retry if if it ever did fail, -+ * because if we don't service the interrupt to clear -+ * it, we will never see another PMU interrupt edge. -+ */ -+ goto reschedule; -+ } -+ -+ /* hey did we just resume? (because we don't get here unless we are -+ * running normally or the first call after resumption) -+ * -+ * pcf50606 resume is really really over now then. -+ */ -+ if (pcf->suspend_state != PCF50606_SS_RUNNING) -+ pcf->suspend_state = PCF50606_SS_RUNNING; - - if (!pcf->coldplug_done) { - DEBUGPC("PMU Coldplug init\n"); -@@ -814,10 +892,26 @@ static void pcf50606_work(struct work_struct *work) - - DEBUGPC("\n"); - -+bail: - pcf->working = 0; - input_sync(pcf->input_dev); - put_device(&pcf->client.dev); - mutex_unlock(&pcf->working_lock); -+ -+ return; -+ -+reschedule: -+ -+ if ((pcf->suspend_state != PCF50606_SS_STARTING_SUSPEND) && -+ (pcf->suspend_state != PCF50606_SS_COMPLETED_SUSPEND)) { -+ msleep(10); -+ dev_info(&pcf->client.dev, "rescheduling interrupt service\n"); -+ } -+ if (!schedule_work(&pcf->work)) -+ dev_err(&pcf->client.dev, "int service reschedule failed\n"); -+ -+ /* we don't put the device here, hold it for next time */ -+ mutex_unlock(&pcf->working_lock); - } - - static irqreturn_t pcf50606_irq(int irq, void *_pcf) -@@ -828,7 +922,7 @@ static irqreturn_t pcf50606_irq(int irq, void *_pcf) - irq, _pcf); - get_device(&pcf->client.dev); - if (!schedule_work(&pcf->work) && !pcf->working) -- dev_dbg(&pcf->client.dev, "work item may be lost\n"); -+ dev_err(&pcf->client.dev, "pcf irq work already queued.\n"); - - return IRQ_HANDLED; - } -@@ -1882,12 +1976,27 @@ static int pcf50606_suspend(struct device *dev, pm_message_t state) - struct pcf50606_data *pcf = i2c_get_clientdata(client); - int i; - -+ /* we suspend once (!) as late as possible in the suspend sequencing */ -+ -+ if ((state.event != PM_EVENT_SUSPEND) || -+ (pcf->suspend_state != PCF50606_SS_RUNNING)) -+ return -EBUSY; -+ - /* The general idea is to power down all unused power supplies, - * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF - * and ALARM */ - - mutex_lock(&pcf->lock); - -+ pcf->suspend_state = PCF50606_SS_STARTING_SUSPEND; -+ -+ /* we are not going to service any further interrupts until we -+ * resume. If the IRQ workqueue is still pending in the background, -+ * it will bail when it sees we set suspend state above. -+ */ -+ -+ disable_irq(pcf->irq); -+ - /* Save all registers that don't "survive" standby state */ - pcf->standby_regs.dcdc1 = __reg_read(pcf, PCF50606_REG_DCDC1); - pcf->standby_regs.dcdc2 = __reg_read(pcf, PCF50606_REG_DCDC2); -@@ -1928,6 +2037,8 @@ static int pcf50606_suspend(struct device *dev, pm_message_t state) - __reg_write(pcf, PCF50606_REG_INT2M, ~INT2M_RESUMERS & 0xff); - __reg_write(pcf, PCF50606_REG_INT3M, ~INT3M_RESUMERS & 0xff); - -+ pcf->suspend_state = PCF50606_SS_COMPLETED_SUSPEND; -+ - mutex_unlock(&pcf->lock); - - return 0; -@@ -1940,6 +2051,8 @@ static int pcf50606_resume(struct device *dev) - - mutex_lock(&pcf->lock); - -+ pcf->suspend_state = PCF50606_SS_STARTING_RESUME; -+ - /* Resume all saved registers that don't "survive" standby state */ - __reg_write(pcf, PCF50606_REG_INT1M, pcf->standby_regs.int1m); - __reg_write(pcf, PCF50606_REG_INT2M, pcf->standby_regs.int2m); -@@ -1958,10 +2071,17 @@ static int pcf50606_resume(struct device *dev) - __reg_write(pcf, PCF50606_REG_ADCC2, pcf->standby_regs.adcc2); - __reg_write(pcf, PCF50606_REG_PWMC1, pcf->standby_regs.pwmc1); - -+ pcf->suspend_state = PCF50606_SS_COMPLETED_RESUME; -+ -+ enable_irq(pcf->irq); -+ - mutex_unlock(&pcf->lock); - -- /* Hack to fix the gta01 power button problem on resume */ -- pcf50606_irq(0, pcf); -+ /* Call PCF work function; this fixes an issue on the gta01 where -+ * the power button "goes away" if it is used to wake the device. -+ */ -+ get_device(&pcf->client.dev); -+ pcf50606_work(&pcf->work); - - return 0; - } -@@ -1999,9 +2119,25 @@ static int pcf50606_plat_remove(struct platform_device *pdev) - return 0; - } - -+/* We have this purely to capture an early indication that we are coming out -+ * of suspend, before our device resume got called; async interrupt service is -+ * interested in this. -+ */ -+ -+static int pcf50606_plat_resume(struct platform_device *pdev) -+{ -+ /* i2c_get_clientdata(to_i2c_client(&pdev->dev)) returns NULL at this -+ * early resume time so we have to use pcf50606_global -+ */ -+ pcf50606_global->suspend_state = PCF50606_SS_RESUMING_BUT_NOT_US_YET; -+ -+ return 0; -+} -+ - static struct platform_driver pcf50606_plat_driver = { - .probe = pcf50606_plat_probe, - .remove = pcf50606_plat_remove, -+ .resume_early = pcf50606_plat_resume, - .driver = { - .owner = THIS_MODULE, - .name = "pcf50606", --- -1.5.6.5 - |