From 27ca1adf36e3015288479bbb9589ea80a96b37f6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Nov 2009 17:47:28 +0100 Subject: [PATCH] jz4740: Cleanup powermanagemnt code. Add inital suspend support. --- .../files-2.6.31/arch/mips/jz4740/gpio.c | 59 ++- .../files-2.6.31/arch/mips/jz4740/irq.c | 56 ++- .../xburst/files-2.6.31/arch/mips/jz4740/pm.c | 349 +----------------- .../files-2.6.31/arch/mips/jz4740/setup.c | 3 - 4 files changed, 112 insertions(+), 355 deletions(-) diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/gpio.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/gpio.c index 67126e1c1..4bdf03708 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/gpio.c +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/gpio.c @@ -66,6 +66,9 @@ #define GPIO_TO_BIT(gpio) BIT(gpio & 0x1f) #define GPIO_TO_REG(gpio, reg) (jz_gpio_base + ((gpio >> 5) << 8) + reg) +#define GPIO_TO_MASK_REG(gpio) GPIO_TO_REG(gpio, 0x20) +#define GPIO_TO_MASK_SET_REG(gpio) GPIO_TO_REG(gpio, 0x24) +#define GPIO_TO_MASK_CLEAR_REG(gpio) GPIO_TO_REG(gpio, 0x28) #define GPIO_TO_PULL_REG(gpio) GPIO_TO_REG(gpio, 0x30) #define GPIO_TO_PULL_SET_REG(gpio) GPIO_TO_REG(gpio, 0x34) #define GPIO_TO_PULL_CLEAR_REG(gpio) GPIO_TO_REG(gpio, 0x38) @@ -81,16 +84,23 @@ - static void __iomem *jz_gpio_base; static spinlock_t jz_gpio_lock; struct jz_gpio_chip { + unsigned int irq; unsigned int irq_base; + uint32_t wakeup; + uint32_t saved[4]; struct gpio_chip gpio_chip; struct irq_chip irq_chip; }; +static struct jz_gpio_chip *jz_irq_to_chip(unsigned int irq) +{ + return get_irq_chip_data(irq); +} + int jz_gpio_set_function(int gpio, enum jz_gpio_function function) { if (function == JZ_GPIO_FUNC_NONE) { @@ -109,8 +119,8 @@ int jz_gpio_set_function(int gpio, enum jz_gpio_function function) writew(GPIO_TO_BIT(gpio), GPIO_TO_SEL_SET_REG(gpio)); break; default: - BUG(); - break; + BUG(); + break; } } @@ -301,6 +311,18 @@ static int jz_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) return 0; } +static int jz_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + struct jz_gpio_chip *chip = jz_irq_to_chip(irq); + if (on) { + chip->wakeup |= IRQ_TO_BIT(irq); + } else { + chip->wakeup &= ~IRQ_TO_BIT(irq); + } + set_irq_wake(chip->irq, on); + return 0; +} + int gpio_to_irq(unsigned gpio) { return JZ_IRQ_GPIO(0) + gpio; @@ -309,7 +331,7 @@ EXPORT_SYMBOL_GPL(gpio_to_irq); int irq_to_gpio(unsigned gpio) { - return IRQ_TO_GPIO(gpio); + return IRQ_TO_GPIO(gpio); } EXPORT_SYMBOL_GPL(irq_to_gpio); @@ -333,6 +355,7 @@ EXPORT_SYMBOL_GPL(irq_to_gpio); .startup = jz_gpio_irq_startup, \ .shutdown = jz_gpio_irq_shutdown, \ .set_type = jz_gpio_irq_set_type, \ + .set_wake = jz_gpio_irq_set_wake, \ }, \ } @@ -352,13 +375,37 @@ int __init jz_gpiolib_init(void) for (i = 0; i < ARRAY_SIZE(jz_gpio_chips); ++i, ++chip) { gpiochip_add(&chip->gpio_chip); - set_irq_chained_handler(JZ_IRQ_INTC_GPIO(i), jz_gpio_irq_demux_handler); + chip->irq = JZ_IRQ_INTC_GPIO(i); + set_irq_chained_handler(chip->irq, jz_gpio_irq_demux_handler); for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio; - ++irq) + ++irq) { set_irq_chip_and_handler(irq, &chip->irq_chip, handle_level_irq); + set_irq_chip_data(irq, chip); + } } printk("JZ GPIO initalized\n"); return 0; } + +void jz_gpiolib_suspend(void) +{ + struct jz_gpio_chip *chip = jz_gpio_chips; + int i, gpio; + for (i = 0; i < ARRAY_SIZE(jz_gpio_chips); ++i, ++chip) { + gpio = chip->gpio_chip.base; + chip->saved[0] = readl(GPIO_TO_MASK_REG(gpio)); + writel(~(chip->wakeup), GPIO_TO_MASK_SET_REG(gpio)); + } +} + +/* TODO: Use sysdev */ +void jz_gpiolib_resume(void) +{ + struct jz_gpio_chip *chip = jz_gpio_chips; + int i, gpio; + for (i = 0; i < ARRAY_SIZE(jz_gpio_chips); ++i, ++chip) { + writel(~(chip->saved[0]), GPIO_TO_MASK_CLEAR_REG(chip->gpio_chip.base)); + } +} diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/irq.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/irq.c index 29ba49a7c..8fc85d70c 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/irq.c +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/irq.c @@ -36,6 +36,8 @@ #include static void __iomem *jz_intc_base; +static uint32_t jz_intc_wakeup; +static uint32_t jz_intc_saved; #define JZ_REG_BASE_INTC 0x10001000 @@ -69,12 +71,23 @@ static void intc_irq_end(unsigned int irq) } } +static int intc_irq_set_wake(unsigned int irq, unsigned int on) +{ + if (on) + jz_intc_wakeup |= IRQ_BIT(irq); + else + jz_intc_wakeup &= ~IRQ_BIT(irq); + + return 0; +} + static struct irq_chip intc_irq_type = { - .name = "INTC", - .mask = intc_irq_mask, - .unmask = intc_irq_unmask, - .ack = intc_irq_ack, - .end = intc_irq_end, + .name = "INTC", + .mask = intc_irq_mask, + .unmask = intc_irq_unmask, + .ack = intc_irq_ack, + .end = intc_irq_end, + .set_wake = intc_irq_set_wake, }; static irqreturn_t jz4740_cascade(int irq, void *data) @@ -83,11 +96,11 @@ static irqreturn_t jz4740_cascade(int irq, void *data) irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING); if (irq_reg) { - generic_handle_irq(ffs(irq_reg) - 1 + JZ_IRQ_BASE); - return IRQ_HANDLED; - } + generic_handle_irq(ffs(irq_reg) - 1 + JZ_IRQ_BASE); + return IRQ_HANDLED; + } - return 0; + return 0; } static struct irqaction jz4740_cascade_action = { @@ -98,7 +111,7 @@ static struct irqaction jz4740_cascade_action = { void __init arch_init_irq(void) { int i; - mips_cpu_irq_init(); + mips_cpu_irq_init(); jz_intc_base = ioremap(JZ_REG_BASE_INTC, 0x14); @@ -113,10 +126,23 @@ void __init arch_init_irq(void) asmlinkage void plat_irq_dispatch(void) { unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; - if (pending & STATUSF_IP2) - jz4740_cascade(2, NULL); - else if(pending & STATUSF_IP3) - do_IRQ(3); - else + if (pending & STATUSF_IP2) + jz4740_cascade(2, NULL); + else if(pending & STATUSF_IP3) + do_IRQ(3); + else spurious_interrupt(); } + +/* TODO: Use sysdev */ +void jz4740_intc_suspend(void) +{ + jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK); + printk("intc wakeup: %d\n", jz_intc_wakeup); + writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK); +} + +void jz4740_intc_resume(void) +{ + writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK); +} diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/pm.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/pm.c index 1655bd384..48a994d60 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/pm.c +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/pm.c @@ -9,7 +9,7 @@ * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. - * + * * This program is distributed in the hope 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 @@ -18,278 +18,41 @@ * 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. - * + * */ #include #include -#include -#include -#include #include #include -#include #include -#undef DEBUG -//#define DEBUG -#ifdef DEBUG -#define dprintk(x...) printk(x) -#else -#define dprintk(x...) -#endif +extern void jz4740_intc_suspend(void); +extern void jz4740_intc_resume(void); +extern void jz_gpiolib_suspend(void); +extern void jz_gpiolib_resume(void); -#define GPIO_WAKEUP 125 /* set SW7(GPIO 125) as WAKEUP key */ - -/* - * __gpio_as_sleep set all pins to pull-disable, and set all pins as input - * except sdram, nand flash pins and the pins which can be used as CS1_N - * to CS4_N for chip select. - */ -#define __gpio_as_sleep() \ -do { \ - REG_GPIO_PXFUNC(1) = ~0x9ff9ffff; \ - REG_GPIO_PXSELC(1) = ~0x9ff9ffff; \ - REG_GPIO_PXDIRC(1) = ~0x9ff9ffff; \ - REG_GPIO_PXPES(1) = 0xffffffff; \ - REG_GPIO_PXFUNC(2) = ~0x37000000; \ - REG_GPIO_PXSELC(2) = ~0x37000000; \ - REG_GPIO_PXDIRC(2) = ~0x37000000; \ - REG_GPIO_PXPES(2) = 0xffffffff; \ - REG_GPIO_PXFUNC(3) = 0xffffffff; \ - REG_GPIO_PXSELC(3) = 0xffffffff; \ - REG_GPIO_PXDIRC(3) = 0xffffffff; \ - REG_GPIO_PXPES(3) = 0xffffffff; \ -} while (0) - -static int jz_pm_do_hibernate(void) +static int jz_pm_enter(suspend_state_t state) { - - /* Mask all interrupts */ - REG_INTC_IMSR = 0xffffffff; - - /* - * RTC Wakeup or 1Hz interrupt can be enabled or disabled - * through RTC driver's ioctl (linux/driver/char/rtc_jz.c). - */ - - /* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */ - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - REG_RTC_HWFCR = (100 << RTC_HWFCR_BIT); - - /* Set reset pin low-level assertion time after wakeup: must > 60ms */ - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - REG_RTC_HRCR = (60 << RTC_HRCR_BIT); /* 60 ms */ - - /* Scratch pad register to be reserved */ - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - REG_RTC_HSPR = 0x12345678; - - /* clear wakeup status register */ - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - REG_RTC_HWRSR = 0x0; - - /* Put CPU to power down mode */ - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - REG_RTC_HCR = RTC_HCR_PD; - - while (!(REG_RTC_RCR & RTC_RCR_WRDY)); - while(1); - - /* We can't get here */ - return 0; -} - -/* NOTES: - * 1: Pins that are floated (NC) should be set as input and pull-enable. - * 2: Pins that are pull-up or pull-down by outside should be set as input - * and pull-disable. - * 3: Pins that are connected to a chip except sdram and nand flash - * should be set as input and pull-disable, too. - */ -static void jz_board_do_sleep(unsigned long *ptr) -{ - unsigned char i; - - /* Print messages of GPIO registers for debug */ - for(i=0;i<4;i++) { - dprintk("run dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ - REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ - REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); - } - - /* Save GPIO registers */ - for(i = 1; i < 4; i++) { - *ptr++ = REG_GPIO_PXFUN(i); - *ptr++ = REG_GPIO_PXSEL(i); - *ptr++ = REG_GPIO_PXDIR(i); - *ptr++ = REG_GPIO_PXPE(i); - *ptr++ = REG_GPIO_PXIM(i); - *ptr++ = REG_GPIO_PXDAT(i); - *ptr++ = REG_GPIO_PXTRG(i); - } - - /* - * Set all pins to pull-disable, and set all pins as input except - * sdram, nand flash pins and the pins which can be used as CS1_N - * to CS4_N for chip select. - */ - __gpio_as_sleep(); - - /* - * Set proper status for GPB25 to GPB28 which can be used as CS1_N to CS4_N. - * Keep the pins' function used for chip select(CS) here according to your - * system to avoid chip select crashing with sdram when resuming from sleep mode. - */ - -#if defined(CONFIG_JZ4740_PAVO) - /* GPB25/CS1_N is used as chip select for nand flash, shouldn't be change. */ - - /* GPB26/CS2_N is connected to nand flash, needn't be changed. */ - - /* GPB27/CS3_N is used as EXT_INT for CS8900 on debug board, it should be set as input.*/ - __gpio_as_input(32+27); - - /* GPB28/CS4_N is used as cs8900's chip select, shouldn't be changed. */ -#endif - - /* - * Enable pull for NC pins here according to your system - */ - -#if defined(CONFIG_JZ4740_PAVO) - /* GPB30-27 <-> J1: WE_N RD_N CS4_N EXT_INT */ - for(i=27;i<31;i++) { - __gpio_enable_pull(32+i); - } - - /* GPC27<-> WAIT_N */ - __gpio_enable_pull(32*2+27); - - /* GPD16<->SD_WP; GPD13-10<->MSC_D0-3; GPD9<->MSC_CMD; GPD8<->MSC_CLK */ - __gpio_enable_pull(32*3+16); - for(i=8;i<14;i++) { - __gpio_enable_pull(32*3+i); - } -#endif - - /* - * If you must set some GPIOs as output to high level or low level, - * you can set them here, using: - * __gpio_as_output(n); - * __gpio_set_pin(n); or __gpio_clear_pin(n); - */ - -#if defined(CONFIG_JZ4740_PAVO) - /* GPD16 which is used as AMPEN_N should be set to high to disable audio amplifier */ - __gpio_set_pin(32*3+4); -#endif - -#ifdef DEBUG - /* Keep uart0 function for printing debug message */ - __gpio_as_uart0(); - - /* Print messages of GPIO registers for debug */ - for(i=0;i<4;i++) { - dprintk("sleep dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ - REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ - REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); - } -#endif -} - -static void jz_board_do_resume(unsigned long *ptr) -{ - unsigned char i; - - /* Restore GPIO registers */ - for(i = 1; i < 4; i++) { - REG_GPIO_PXFUNS(i) = *ptr; - REG_GPIO_PXFUNC(i) = ~(*ptr++); - - REG_GPIO_PXSELS(i) = *ptr; - REG_GPIO_PXSELC(i) = ~(*ptr++); - - REG_GPIO_PXDIRS(i) = *ptr; - REG_GPIO_PXDIRC(i) = ~(*ptr++); - - REG_GPIO_PXPES(i) = *ptr; - REG_GPIO_PXPEC(i) = ~(*ptr++); - - REG_GPIO_PXIMS(i)=*ptr; - REG_GPIO_PXIMC(i)=~(*ptr++); - - REG_GPIO_PXDATS(i)=*ptr; - REG_GPIO_PXDATC(i)=~(*ptr++); - - REG_GPIO_PXTRGS(i)=*ptr; - REG_GPIO_PXTRGC(i)=~(*ptr++); - } - - /* Print messages of GPIO registers for debug */ - for(i=0;i<4;i++) { - dprintk("resume dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ - REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ - REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); - } -} - - - -static int jz_pm_do_sleep(void) -{ unsigned long delta; unsigned long nfcsr = REG_EMC_NFCSR; - unsigned long scr = REG_CPM_SCR; - unsigned long imr = REG_INTC_IMR; - unsigned long sadc = REG_SADC_ENA; - unsigned long sleep_gpio_save[7*3]; + uint32_t scr = REG_CPM_SCR; + uint32_t sleep_gpio_save[7*3]; /* Preserve current time */ delta = xtime.tv_sec - REG_RTC_RSR; - /* Disable nand flash */ + /* Disable nand flash */ REG_EMC_NFCSR = ~0xff; - /* stop sadc */ - REG_SADC_ENA &= ~0x7; - while((REG_SADC_ENA & 0x7) != 0); udelay(100); - /*stop udc and usb*/ + /*stop udc and usb*/ REG_CPM_SCR &= ~( 1<<6 | 1<<7); REG_CPM_SCR |= 0<<6 | 1<<7; - /* Sleep on-board modules */ - jz_board_do_sleep(sleep_gpio_save); - - /* Mask all interrupts */ - REG_INTC_IMSR = 0xffffffff; - - /* Just allow following interrupts to wakeup the system. - * Note: modify this according to your system. - */ - - /* enable RTC alarm */ - __intc_unmask_irq(JZ_IRQ_RTC); -#if 0 - /* make system wake up after n seconds by RTC alarm */ - unsigned int v, n; - n = 10; - while (!__rtc_write_ready()); - __rtc_enable_alarm(); - while (!__rtc_write_ready()); - __rtc_enable_alarm_irq(); - while (!__rtc_write_ready()); - v = __rtc_get_second(); - while (!__rtc_write_ready()); - __rtc_set_alarm_second(v+n); -#endif - - /* WAKEUP key */ - __gpio_as_irq_rise_edge(GPIO_WAKEUP); - __gpio_unmask_irq(GPIO_WAKEUP); - __intc_unmask_irq(JZ_IRQ_GPIO3); /* IRQ_GPIOn depends on GPIO_WAKEUP */ + jz_gpiolib_suspend(); + jz4740_intc_suspend(); /* Enter SLEEP mode */ REG_CPM_LCR &= ~CPM_LCR_LPM_MASK; @@ -302,18 +65,11 @@ static int jz_pm_do_sleep(void) REG_CPM_LCR &= ~CPM_LCR_LPM_MASK; REG_CPM_LCR |= CPM_LCR_LPM_IDLE; - /* Restore nand flash control register */ + /* Restore nand flash control register */ REG_EMC_NFCSR = nfcsr; - /* Restore interrupts */ - REG_INTC_IMSR = imr; - REG_INTC_IMCR = ~imr; - - /* Restore sadc */ - REG_SADC_ENA = sadc; - - /* Resume on-board modules */ - jz_board_do_resume(sleep_gpio_save); + jz4740_intc_resume(); + jz_gpiolib_resume(); /* Restore sleep control register */ REG_CPM_SCR = scr; @@ -324,87 +80,18 @@ static int jz_pm_do_sleep(void) return 0; } -/* Put CPU to HIBERNATE mode */ -int jz_pm_hibernate(void) -{ - printk("Put CPU into hibernate mode.\n"); - return jz_pm_do_hibernate(); -} - -#ifndef CONFIG_JZ_POWEROFF -static irqreturn_t pm_irq_handler (int irq, void *dev_id) -{ - return IRQ_HANDLED; -} -#endif - -/* Put CPU to SLEEP mode */ -int jz_pm_sleep(void) -{ - int retval; - -#ifndef CONFIG_JZ_POWEROFF - if ((retval = request_irq (IRQ_GPIO_0 + GPIO_WAKEUP, pm_irq_handler, IRQF_DISABLED, - "PM", NULL))) { - printk ("PM could not get IRQ for GPIO_WAKEUP\n"); - return retval; - } -#endif - printk("Put CPU into sleep mode.\n"); - retval = jz_pm_do_sleep(); - -#ifndef CONFIG_JZ_POWEROFF - free_irq (IRQ_GPIO_0 + GPIO_WAKEUP, NULL); -#endif - - return retval; -} - - -/* - * valid states, only support standby(sleep) and mem(hibernate) - */ -static int jz_pm_valid(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_STANDBY: - case PM_SUSPEND_MEM: - return 1; - default: - return 0; - } -} - -/* - * Jz CPU enter save power mode - */ -static int jz_pm_enter(suspend_state_t state) -{ - if (state == PM_SUSPEND_STANDBY) - jz_pm_sleep(); - else - jz_pm_hibernate(); - return 0; -} - - static struct platform_suspend_ops jz_pm_ops = { - .valid = jz_pm_valid, + .valid = suspend_valid_only_mem, .enter = jz_pm_enter, }; - /* * Initialize power interface */ int __init jz_pm_init(void) { - printk("Power Management for JZ\n"); - suspend_set_ops(&jz_pm_ops); return 0; } - -//module_init(jz_pm_init); - +late_initcall(jz_pm_init); diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/setup.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/setup.c index ebbf2543c..409b1d3f2 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/setup.c +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/setup.c @@ -174,9 +174,6 @@ void __init plat_mem_setup(void) _machine_restart = jz_restart; _machine_halt = jz_halt; pm_power_off = jz_power_off; -#ifdef CONFIG_PM - jz_pm_init(); -#endif jz_soc_setup(); jz_serial_setup(); }