|
@@ -35,7 +35,6 @@
|
|
|
#include <linux/jiffies.h>
|
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
-#include <asm/cacheflush.h>
|
|
|
#include <asm/mach-au1x00/au1000.h>
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
@@ -47,8 +46,6 @@
|
|
|
#define DPRINTK(fmt, args...)
|
|
|
#endif
|
|
|
|
|
|
-static void au1000_calibrate_delay(void);
|
|
|
-
|
|
|
extern unsigned long save_local_and_disable(int controller);
|
|
|
extern void restore_local_and_enable(int controller, unsigned long mask);
|
|
|
|
|
@@ -64,17 +61,15 @@ static DEFINE_SPINLOCK(pm_lock);
|
|
|
* We only have to save/restore registers that aren't otherwise
|
|
|
* done as part of a driver pm_* function.
|
|
|
*/
|
|
|
-static unsigned int sleep_aux_pll_cntrl;
|
|
|
-static unsigned int sleep_cpu_pll_cntrl;
|
|
|
-static unsigned int sleep_pin_function;
|
|
|
-static unsigned int sleep_uart0_inten;
|
|
|
-static unsigned int sleep_uart0_fifoctl;
|
|
|
-static unsigned int sleep_uart0_linectl;
|
|
|
-static unsigned int sleep_uart0_clkdiv;
|
|
|
-static unsigned int sleep_uart0_enable;
|
|
|
-static unsigned int sleep_usbhost_enable;
|
|
|
-static unsigned int sleep_usbdev_enable;
|
|
|
-static unsigned int sleep_static_memctlr[4][3];
|
|
|
+static unsigned int sleep_uart0_inten;
|
|
|
+static unsigned int sleep_uart0_fifoctl;
|
|
|
+static unsigned int sleep_uart0_linectl;
|
|
|
+static unsigned int sleep_uart0_clkdiv;
|
|
|
+static unsigned int sleep_uart0_enable;
|
|
|
+static unsigned int sleep_usb[2];
|
|
|
+static unsigned int sleep_sys_clocks[5];
|
|
|
+static unsigned int sleep_sys_pinfunc;
|
|
|
+static unsigned int sleep_static_memctlr[4][3];
|
|
|
|
|
|
/*
|
|
|
* Define this to cause the value you write to /proc/sys/pm/sleep to
|
|
@@ -108,31 +103,45 @@ static void save_core_regs(void)
|
|
|
sleep_uart0_linectl = au_readl(UART0_ADDR + UART_LCR);
|
|
|
sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK);
|
|
|
sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL);
|
|
|
+ au_sync();
|
|
|
|
|
|
+#ifndef CONFIG_SOC_AU1200
|
|
|
/* Shutdown USB host/device. */
|
|
|
- sleep_usbhost_enable = au_readl(USB_HOST_CONFIG);
|
|
|
+ sleep_usb[0] = au_readl(USB_HOST_CONFIG);
|
|
|
|
|
|
/* There appears to be some undocumented reset register.... */
|
|
|
- au_writel(0, 0xb0100004); au_sync();
|
|
|
- au_writel(0, USB_HOST_CONFIG); au_sync();
|
|
|
+ au_writel(0, 0xb0100004);
|
|
|
+ au_sync();
|
|
|
+ au_writel(0, USB_HOST_CONFIG);
|
|
|
+ au_sync();
|
|
|
+
|
|
|
+ sleep_usb[1] = au_readl(USBD_ENABLE);
|
|
|
+ au_writel(0, USBD_ENABLE);
|
|
|
+ au_sync();
|
|
|
+
|
|
|
+#else /* AU1200 */
|
|
|
|
|
|
- sleep_usbdev_enable = au_readl(USBD_ENABLE);
|
|
|
- au_writel(0, USBD_ENABLE); au_sync();
|
|
|
+ /* enable access to OTG mmio so we can save OTG CAP/MUX.
|
|
|
+ * FIXME: write an OTG driver and move this stuff there!
|
|
|
+ */
|
|
|
+ au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4);
|
|
|
+ au_sync();
|
|
|
+ sleep_usb[0] = au_readl(0xb4020020); /* OTG_CAP */
|
|
|
+ sleep_usb[1] = au_readl(0xb4020024); /* OTG_MUX */
|
|
|
+#endif
|
|
|
|
|
|
/* Save interrupt controller state. */
|
|
|
save_au1xxx_intctl();
|
|
|
|
|
|
/* Clocks and PLLs. */
|
|
|
- sleep_aux_pll_cntrl = au_readl(SYS_AUXPLL);
|
|
|
+ sleep_sys_clocks[0] = au_readl(SYS_FREQCTRL0);
|
|
|
+ sleep_sys_clocks[1] = au_readl(SYS_FREQCTRL1);
|
|
|
+ sleep_sys_clocks[2] = au_readl(SYS_CLKSRC);
|
|
|
+ sleep_sys_clocks[3] = au_readl(SYS_CPUPLL);
|
|
|
+ sleep_sys_clocks[4] = au_readl(SYS_AUXPLL);
|
|
|
|
|
|
- /*
|
|
|
- * We don't really need to do this one, but unless we
|
|
|
- * write it again it won't have a valid value if we
|
|
|
- * happen to read it.
|
|
|
- */
|
|
|
- sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL);
|
|
|
-
|
|
|
- sleep_pin_function = au_readl(SYS_PINFUNC);
|
|
|
+ /* pin mux config */
|
|
|
+ sleep_sys_pinfunc = au_readl(SYS_PINFUNC);
|
|
|
|
|
|
/* Save the static memory controller configuration. */
|
|
|
sleep_static_memctlr[0][0] = au_readl(MEM_STCFG0);
|
|
@@ -151,12 +160,37 @@ static void save_core_regs(void)
|
|
|
|
|
|
static void restore_core_regs(void)
|
|
|
{
|
|
|
- extern void restore_au1xxx_intctl(void);
|
|
|
- extern void wakeup_counter0_adjust(void);
|
|
|
+ /* restore clock configuration. Writing CPUPLL last will
|
|
|
+ * stall a bit and stabilize other clocks (unless this is
|
|
|
+ * one of those Au1000 with a write-only PLL, where we dont
|
|
|
+ * have a valid value)
|
|
|
+ */
|
|
|
+ au_writel(sleep_sys_clocks[0], SYS_FREQCTRL0);
|
|
|
+ au_writel(sleep_sys_clocks[1], SYS_FREQCTRL1);
|
|
|
+ au_writel(sleep_sys_clocks[2], SYS_CLKSRC);
|
|
|
+ au_writel(sleep_sys_clocks[4], SYS_AUXPLL);
|
|
|
+ if (!au1xxx_cpu_has_pll_wo())
|
|
|
+ au_writel(sleep_sys_clocks[3], SYS_CPUPLL);
|
|
|
+ au_sync();
|
|
|
+
|
|
|
+ au_writel(sleep_sys_pinfunc, SYS_PINFUNC);
|
|
|
+ au_sync();
|
|
|
+
|
|
|
+#ifndef CONFIG_SOC_AU1200
|
|
|
+ au_writel(sleep_usb[0], USB_HOST_CONFIG);
|
|
|
+ au_writel(sleep_usb[1], USBD_ENABLE);
|
|
|
+ au_sync();
|
|
|
+#else
|
|
|
+ /* enable accces to OTG memory */
|
|
|
+ au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4);
|
|
|
+ au_sync();
|
|
|
|
|
|
- au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync();
|
|
|
- au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync();
|
|
|
- au_writel(sleep_pin_function, SYS_PINFUNC); au_sync();
|
|
|
+ /* restore OTG caps and port mux. */
|
|
|
+ au_writel(sleep_usb[0], 0xb4020020 + 0); /* OTG_CAP */
|
|
|
+ au_sync();
|
|
|
+ au_writel(sleep_usb[1], 0xb4020020 + 4); /* OTG_MUX */
|
|
|
+ au_sync();
|
|
|
+#endif
|
|
|
|
|
|
/* Restore the static memory controller configuration. */
|
|
|
au_writel(sleep_static_memctlr[0][0], MEM_STCFG0);
|
|
@@ -196,16 +230,45 @@ void wakeup_from_suspend(void)
|
|
|
suspend_mode = 0;
|
|
|
}
|
|
|
|
|
|
-int au_sleep(void)
|
|
|
+void au_sleep(void)
|
|
|
+{
|
|
|
+ save_core_regs();
|
|
|
+ au1xxx_save_and_sleep();
|
|
|
+ restore_core_regs();
|
|
|
+}
|
|
|
+
|
|
|
+static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
|
|
|
+ void __user *buffer, size_t *len, loff_t *ppos)
|
|
|
{
|
|
|
unsigned long wakeup, flags;
|
|
|
- extern void save_and_sleep(void);
|
|
|
+ int ret;
|
|
|
+#ifdef SLEEP_TEST_TIMEOUT
|
|
|
+#define TMPBUFLEN2 16
|
|
|
+ char buf[TMPBUFLEN2], *p;
|
|
|
+#endif
|
|
|
|
|
|
spin_lock_irqsave(&pm_lock, flags);
|
|
|
|
|
|
- save_core_regs();
|
|
|
+ if (!write) {
|
|
|
+ *len = 0;
|
|
|
+ ret = 0;
|
|
|
+ goto out_unlock;
|
|
|
+ };
|
|
|
|
|
|
- flush_cache_all();
|
|
|
+#ifdef SLEEP_TEST_TIMEOUT
|
|
|
+ if (*len > TMPBUFLEN2 - 1) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+ if (copy_from_user(buf, buffer, *len)) {
|
|
|
+ return -EFAULT;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+ buf[*len] = 0;
|
|
|
+ p = buf;
|
|
|
+ sleep_ticks = simple_strtoul(p, &p, 0);
|
|
|
+ wakeup_counter0_set(sleep_ticks);
|
|
|
+#endif
|
|
|
|
|
|
/**
|
|
|
** The code below is all system dependent and we should probably
|
|
@@ -223,9 +286,6 @@ int au_sleep(void)
|
|
|
wakeup |= 1 << 6; /* turn on GPIO 6 wakeup */
|
|
|
#else
|
|
|
/* For testing, allow match20 to wake us up. */
|
|
|
-#ifdef SLEEP_TEST_TIMEOUT
|
|
|
- wakeup_counter0_set(sleep_ticks);
|
|
|
-#endif
|
|
|
wakeup = 1 << 8; /* turn on match20 wakeup */
|
|
|
wakeup = 0;
|
|
|
#endif
|
|
@@ -234,41 +294,62 @@ int au_sleep(void)
|
|
|
au_writel(wakeup, SYS_WAKEMSK);
|
|
|
au_sync();
|
|
|
|
|
|
- save_and_sleep();
|
|
|
+ au_sleep();
|
|
|
+ ret = 0;
|
|
|
|
|
|
- /*
|
|
|
- * After a wakeup, the cpu vectors back to 0x1fc00000, so
|
|
|
- * it's up to the boot code to get us back here.
|
|
|
- */
|
|
|
- restore_core_regs();
|
|
|
+out_unlock:
|
|
|
spin_unlock_irqrestore(&pm_lock, flags);
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
|
|
|
- void __user *buffer, size_t *len, loff_t *ppos)
|
|
|
+#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
|
|
|
+
|
|
|
+/*
|
|
|
+ * This is right out of init/main.c
|
|
|
+ */
|
|
|
+
|
|
|
+/*
|
|
|
+ * This is the number of bits of precision for the loops_per_jiffy.
|
|
|
+ * Each bit takes on average 1.5/HZ seconds. This (like the original)
|
|
|
+ * is a little better than 1%.
|
|
|
+ */
|
|
|
+#define LPS_PREC 8
|
|
|
+
|
|
|
+static void au1000_calibrate_delay(void)
|
|
|
{
|
|
|
-#ifdef SLEEP_TEST_TIMEOUT
|
|
|
-#define TMPBUFLEN2 16
|
|
|
- char buf[TMPBUFLEN2], *p;
|
|
|
-#endif
|
|
|
+ unsigned long ticks, loopbit;
|
|
|
+ int lps_precision = LPS_PREC;
|
|
|
|
|
|
- if (!write)
|
|
|
- *len = 0;
|
|
|
- else {
|
|
|
-#ifdef SLEEP_TEST_TIMEOUT
|
|
|
- if (*len > TMPBUFLEN2 - 1)
|
|
|
- return -EFAULT;
|
|
|
- if (copy_from_user(buf, buffer, *len))
|
|
|
- return -EFAULT;
|
|
|
- buf[*len] = 0;
|
|
|
- p = buf;
|
|
|
- sleep_ticks = simple_strtoul(p, &p, 0);
|
|
|
-#endif
|
|
|
+ loops_per_jiffy = 1 << 12;
|
|
|
|
|
|
- au_sleep();
|
|
|
+ while (loops_per_jiffy <<= 1) {
|
|
|
+ /* Wait for "start of" clock tick */
|
|
|
+ ticks = jiffies;
|
|
|
+ while (ticks == jiffies)
|
|
|
+ /* nothing */ ;
|
|
|
+ /* Go ... */
|
|
|
+ ticks = jiffies;
|
|
|
+ __delay(loops_per_jiffy);
|
|
|
+ ticks = jiffies - ticks;
|
|
|
+ if (ticks)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do a binary approximation to get loops_per_jiffy set to be equal
|
|
|
+ * one clock (up to lps_precision bits)
|
|
|
+ */
|
|
|
+ loops_per_jiffy >>= 1;
|
|
|
+ loopbit = loops_per_jiffy;
|
|
|
+ while (lps_precision-- && (loopbit >>= 1)) {
|
|
|
+ loops_per_jiffy |= loopbit;
|
|
|
+ ticks = jiffies;
|
|
|
+ while (ticks == jiffies);
|
|
|
+ ticks = jiffies;
|
|
|
+ __delay(loops_per_jiffy);
|
|
|
+ if (jiffies != ticks) /* longer than 1 tick */
|
|
|
+ loops_per_jiffy &= ~loopbit;
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
|
|
@@ -377,7 +458,7 @@ static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
|
|
|
|
|
|
return retval;
|
|
|
}
|
|
|
-
|
|
|
+#endif
|
|
|
|
|
|
static struct ctl_table pm_table[] = {
|
|
|
{
|
|
@@ -388,6 +469,7 @@ static struct ctl_table pm_table[] = {
|
|
|
.mode = 0600,
|
|
|
.proc_handler = &pm_do_sleep
|
|
|
},
|
|
|
+#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
|
|
|
{
|
|
|
.ctl_name = CTL_UNNUMBERED,
|
|
|
.procname = "freq",
|
|
@@ -396,6 +478,7 @@ static struct ctl_table pm_table[] = {
|
|
|
.mode = 0600,
|
|
|
.proc_handler = &pm_do_freq
|
|
|
},
|
|
|
+#endif
|
|
|
{}
|
|
|
};
|
|
|
|
|
@@ -429,51 +512,4 @@ static int __init pm_init(void)
|
|
|
|
|
|
__initcall(pm_init);
|
|
|
|
|
|
-/*
|
|
|
- * This is right out of init/main.c
|
|
|
- */
|
|
|
-
|
|
|
-/*
|
|
|
- * This is the number of bits of precision for the loops_per_jiffy.
|
|
|
- * Each bit takes on average 1.5/HZ seconds. This (like the original)
|
|
|
- * is a little better than 1%.
|
|
|
- */
|
|
|
-#define LPS_PREC 8
|
|
|
-
|
|
|
-static void au1000_calibrate_delay(void)
|
|
|
-{
|
|
|
- unsigned long ticks, loopbit;
|
|
|
- int lps_precision = LPS_PREC;
|
|
|
-
|
|
|
- loops_per_jiffy = 1 << 12;
|
|
|
-
|
|
|
- while (loops_per_jiffy <<= 1) {
|
|
|
- /* Wait for "start of" clock tick */
|
|
|
- ticks = jiffies;
|
|
|
- while (ticks == jiffies)
|
|
|
- /* nothing */ ;
|
|
|
- /* Go ... */
|
|
|
- ticks = jiffies;
|
|
|
- __delay(loops_per_jiffy);
|
|
|
- ticks = jiffies - ticks;
|
|
|
- if (ticks)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Do a binary approximation to get loops_per_jiffy set to be equal
|
|
|
- * one clock (up to lps_precision bits)
|
|
|
- */
|
|
|
- loops_per_jiffy >>= 1;
|
|
|
- loopbit = loops_per_jiffy;
|
|
|
- while (lps_precision-- && (loopbit >>= 1)) {
|
|
|
- loops_per_jiffy |= loopbit;
|
|
|
- ticks = jiffies;
|
|
|
- while (ticks == jiffies);
|
|
|
- ticks = jiffies;
|
|
|
- __delay(loops_per_jiffy);
|
|
|
- if (jiffies != ticks) /* longer than 1 tick */
|
|
|
- loops_per_jiffy &= ~loopbit;
|
|
|
- }
|
|
|
-}
|
|
|
#endif /* CONFIG_PM */
|