diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-12-12 00:24:30 +0000 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-05-07 11:04:56 +0100 |
commit | d87964c46005ccb04754f6309df0fd8f67b08c6d (patch) | |
tree | d35fc1fb3660dca0cdb236ef14ba27dcd69618f2 /arch/arm/plat-s3c24xx | |
parent | 966bcc14386000e8b4dc7bbb426910bcb55a8588 (diff) |
[ARM] S3C: GPIO PM core GPIOlib integration
Move the GPIO suspend/resume support inline with the gpiolib support
so that it will work with both the S3C24XX and S3C64XX series.
The s3c_gpio_chip is extended to have a pm callback and a save block
to keep the state of the GPIO over suspend, and the code from the
s3c24xx implementation is added to a new common file.
The suspend process now uses the list of registered chips to go through
saving and restoring each one as appropriate, using the pm callback to
select the appropriate routine depending on the type of control register
present.
This change also means that any additional GPIO added should not require
changes to the PM.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r-- | arch/arm/plat-s3c24xx/gpiolib.c | 8 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/pm.c | 213 |
2 files changed, 8 insertions, 213 deletions
diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c index 5c0491bf738b..4bac12dc0733 100644 --- a/arch/arm/plat-s3c24xx/gpiolib.c +++ b/arch/arm/plat-s3c24xx/gpiolib.c @@ -22,6 +22,7 @@ #include <mach/gpio-core.h> #include <mach/hardware.h> #include <asm/irq.h> +#include <plat/pm.h> #include <mach/regs-gpio.h> @@ -78,6 +79,7 @@ static int s3c24xx_gpiolib_bankg_toirq(struct gpio_chip *chip, unsigned offset) struct s3c_gpio_chip s3c24xx_gpios[] = { [0] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPA0), + .pm = __gpio_pm(&s3c_gpio_pm_1bit), .chip = { .base = S3C2410_GPA0, .owner = THIS_MODULE, @@ -89,6 +91,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [1] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPB0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPB0, .owner = THIS_MODULE, @@ -98,6 +101,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [2] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPC0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPC0, .owner = THIS_MODULE, @@ -107,6 +111,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [3] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPD0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPD0, .owner = THIS_MODULE, @@ -116,6 +121,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [4] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPE0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPE0, .label = "GPIOE", @@ -125,6 +131,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [5] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPF0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPF0, .owner = THIS_MODULE, @@ -135,6 +142,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, [6] = { .base = S3C24XX_GPIO_BASE(S3C2410_GPG0), + .pm = __gpio_pm(&s3c_gpio_pm_2bit), .chip = { .base = S3C2410_GPG0, .owner = THIS_MODULE, diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c index 062a29339a91..5135c40a1b90 100644 --- a/arch/arm/plat-s3c24xx/pm.c +++ b/arch/arm/plat-s3c24xx/pm.c @@ -75,43 +75,10 @@ static struct sleep_save core_save[] = { SAVE_ITEM(S3C2410_CLKSLOW), }; -static struct gpio_sleep { - void __iomem *base; - unsigned int gpcon; - unsigned int gpdat; - unsigned int gpup; -} gpio_save[] = { - [0] = { - .base = S3C2410_GPACON, - }, - [1] = { - .base = S3C2410_GPBCON, - }, - [2] = { - .base = S3C2410_GPCCON, - }, - [3] = { - .base = S3C2410_GPDCON, - }, - [4] = { - .base = S3C2410_GPECON, - }, - [5] = { - .base = S3C2410_GPFCON, - }, - [6] = { - .base = S3C2410_GPGCON, - }, - [7] = { - .base = S3C2410_GPHCON, - }, -}; - static struct sleep_save misc_save[] = { SAVE_ITEM(S3C2410_DCLKCON), }; - /* s3c_pm_check_resume_pin * * check to see if the pin is configured correctly for sleep mode, and @@ -165,186 +132,6 @@ void s3c_pm_configure_extint(void) } } -/* offsets for CON/DAT/UP registers */ - -#define OFFS_CON (S3C2410_GPACON - S3C2410_GPACON) -#define OFFS_DAT (S3C2410_GPADAT - S3C2410_GPACON) -#define OFFS_UP (S3C2410_GPBUP - S3C2410_GPBCON) - -/* s3c_pm_save_gpios() - * - * Save the state of the GPIOs - */ - -void s3c_pm_save_gpios(void) -{ - struct gpio_sleep *gps = gpio_save; - unsigned int gpio; - - for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) { - void __iomem *base = gps->base; - - gps->gpcon = __raw_readl(base + OFFS_CON); - gps->gpdat = __raw_readl(base + OFFS_DAT); - - if (gpio > 0) - gps->gpup = __raw_readl(base + OFFS_UP); - - } -} - -/* Test whether the given masked+shifted bits of an GPIO configuration - * are one of the SFN (special function) modes. */ - -static inline int is_sfn(unsigned long con) -{ - return (con == 2 || con == 3); -} - -/* Test if the given masked+shifted GPIO configuration is an input */ - -static inline int is_in(unsigned long con) -{ - return con == 0; -} - -/* Test if the given masked+shifted GPIO configuration is an output */ - -static inline int is_out(unsigned long con) -{ - return con == 1; -} - -/** - * s3c2410_pm_restore_gpio() - restore the given GPIO bank - * @index: The number of the GPIO bank being resumed. - * @gps: The sleep confgiuration for the bank. - * - * Restore one of the GPIO banks that was saved during suspend. This is - * not as simple as once thought, due to the possibility of glitches - * from the order that the CON and DAT registers are set in. - * - * The three states the pin can be are {IN,OUT,SFN} which gives us 9 - * combinations of changes to check. Three of these, if the pin stays - * in the same configuration can be discounted. This leaves us with - * the following: - * - * { IN => OUT } Change DAT first - * { IN => SFN } Change CON first - * { OUT => SFN } Change CON first, so new data will not glitch - * { OUT => IN } Change CON first, so new data will not glitch - * { SFN => IN } Change CON first - * { SFN => OUT } Change DAT first, so new data will not glitch [1] - * - * We do not currently deal with the UP registers as these control - * weak resistors, so a small delay in change should not need to bring - * these into the calculations. - * - * [1] this assumes that writing to a pin DAT whilst in SFN will set the - * state for when it is next output. - */ - -static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps) -{ - void __iomem *base = gps->base; - unsigned long gps_gpcon = gps->gpcon; - unsigned long gps_gpdat = gps->gpdat; - unsigned long old_gpcon; - unsigned long old_gpdat; - unsigned long old_gpup = 0x0; - unsigned long gpcon; - int nr; - - old_gpcon = __raw_readl(base + OFFS_CON); - old_gpdat = __raw_readl(base + OFFS_DAT); - - if (base == S3C2410_GPACON) { - /* GPACON only has one bit per control / data and no PULLUPs. - * GPACON[x] = 0 => Output, 1 => SFN */ - - /* first set all SFN bits to SFN */ - - gpcon = old_gpcon | gps->gpcon; - __raw_writel(gpcon, base + OFFS_CON); - - /* now set all the other bits */ - - __raw_writel(gps_gpdat, base + OFFS_DAT); - __raw_writel(gps_gpcon, base + OFFS_CON); - } else { - unsigned long old, new, mask; - unsigned long change_mask = 0x0; - - old_gpup = __raw_readl(base + OFFS_UP); - - /* Create a change_mask of all the items that need to have - * their CON value changed before their DAT value, so that - * we minimise the work between the two settings. - */ - - for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) { - old = (old_gpcon & mask) >> nr; - new = (gps_gpcon & mask) >> nr; - - /* If there is no change, then skip */ - - if (old == new) - continue; - - /* If both are special function, then skip */ - - if (is_sfn(old) && is_sfn(new)) - continue; - - /* Change is IN => OUT, do not change now */ - - if (is_in(old) && is_out(new)) - continue; - - /* Change is SFN => OUT, do not change now */ - - if (is_sfn(old) && is_out(new)) - continue; - - /* We should now be at the case of IN=>SFN, - * OUT=>SFN, OUT=>IN, SFN=>IN. */ - - change_mask |= mask; - } - - /* Write the new CON settings */ - - gpcon = old_gpcon & ~change_mask; - gpcon |= gps_gpcon & change_mask; - - __raw_writel(gpcon, base + OFFS_CON); - - /* Now change any items that require DAT,CON */ - - __raw_writel(gps_gpdat, base + OFFS_DAT); - __raw_writel(gps_gpcon, base + OFFS_CON); - __raw_writel(gps->gpup, base + OFFS_UP); - } - - S3C_PMDBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n", - index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); -} - - -/** s3c2410_pm_restore_gpios() - * - * Restore the state of the GPIOs - */ - -void s3c_pm_restore_gpios(void) -{ - struct gpio_sleep *gps = gpio_save; - int gpio; - - for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) { - s3c2410_pm_restore_gpio(gpio, gps); - } -} void s3c_pm_restore_core(void) { |