power.c 14.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * BRIEF MODULE DESCRIPTION
3
 *	Au1xx0 Power Management routines.
L
Linus Torvalds 已提交
4
 *
5 6
 * Copyright 2001, 2008 MontaVista Software Inc.
 * Author: MontaVista Software, Inc. <source@mvista.com>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 *  Some of the routines are right out of init/main.c, whose
 *  copyrights apply here.
 *
 *  This program is free software; you can redistribute	 it and/or modify it
 *  under  the terms of	 the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the	License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
 *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
 *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */
31

L
Linus Torvalds 已提交
32 33 34
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
P
Pete Popov 已提交
35
#include <linux/jiffies.h>
L
Linus Torvalds 已提交
36 37 38 39 40 41 42

#include <asm/uaccess.h>
#include <asm/mach-au1x00/au1000.h>

#ifdef CONFIG_PM

#define DEBUG 1
43 44
#ifdef	DEBUG
#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __func__, ## args)
L
Linus Torvalds 已提交
45
#else
46
#define DPRINTK(fmt, args...)
L
Linus Torvalds 已提交
47 48 49 50 51 52 53
#endif

extern unsigned long save_local_and_disable(int controller);
extern void restore_local_and_enable(int controller, unsigned long mask);

static DEFINE_SPINLOCK(pm_lock);

54 55
/*
 * We need to save/restore a bunch of core registers that are
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63
 * either volatile or reset to some state across a processor sleep.
 * If reading a register doesn't provide a proper result for a
 * later restore, we have to provide a function for loading that
 * register and save a copy.
 *
 * We only have to save/restore registers that aren't otherwise
 * done as part of a driver pm_* function.
 */
64 65 66 67 68 69 70 71 72
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];
L
Linus Torvalds 已提交
73

74 75
/*
 * Define this to cause the value you write to /proc/sys/pm/sleep to
L
Linus Torvalds 已提交
76 77 78 79 80
 * set the TOY timer for the amount of time you want to sleep.
 * This is done mainly for testing, but may be useful in other cases.
 * The value is number of 32KHz ticks to sleep.
 */
#define SLEEP_TEST_TIMEOUT 1
81 82
#ifdef	SLEEP_TEST_TIMEOUT
static int sleep_ticks;
83 84 85 86 87
static void wakeup_counter0_set(int ticks)
{
	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
	au_sync();
}
L
Linus Torvalds 已提交
88 89
#endif

90
static void save_core_regs(void)
L
Linus Torvalds 已提交
91 92 93 94
{
	extern void save_au1xxx_intctl(void);
	extern void pm_eth0_shutdown(void);

95 96
	/*
	 * Do the serial ports.....these really should be a pm_*
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105
	 * registered function by the driver......but of course the
	 * standard serial driver doesn't understand our Au1xxx
	 * unique registers.
	 */
	sleep_uart0_inten = au_readl(UART0_ADDR + UART_IER);
	sleep_uart0_fifoctl = au_readl(UART0_ADDR + UART_FCR);
	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);
106
	au_sync();
L
Linus Torvalds 已提交
107

108
#ifndef CONFIG_SOC_AU1200
109
	/* Shutdown USB host/device. */
110
	sleep_usb[0] = au_readl(USB_HOST_CONFIG);
L
Linus Torvalds 已提交
111

112
	/* There appears to be some undocumented reset register.... */
113 114 115 116 117 118 119 120 121 122
	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 */
L
Linus Torvalds 已提交
123

124 125 126 127 128 129 130 131
	/* 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
L
Linus Torvalds 已提交
132

133
	/* Save interrupt controller state. */
L
Linus Torvalds 已提交
134 135
	save_au1xxx_intctl();

136
	/* Clocks and PLLs. */
137 138 139 140 141
	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);
L
Linus Torvalds 已提交
142

143 144
	/* pin mux config */
	sleep_sys_pinfunc = au_readl(SYS_PINFUNC);
L
Linus Torvalds 已提交
145

146
	/* Save the static memory controller configuration. */
L
Linus Torvalds 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160
	sleep_static_memctlr[0][0] = au_readl(MEM_STCFG0);
	sleep_static_memctlr[0][1] = au_readl(MEM_STTIME0);
	sleep_static_memctlr[0][2] = au_readl(MEM_STADDR0);
	sleep_static_memctlr[1][0] = au_readl(MEM_STCFG1);
	sleep_static_memctlr[1][1] = au_readl(MEM_STTIME1);
	sleep_static_memctlr[1][2] = au_readl(MEM_STADDR1);
	sleep_static_memctlr[2][0] = au_readl(MEM_STCFG2);
	sleep_static_memctlr[2][1] = au_readl(MEM_STTIME2);
	sleep_static_memctlr[2][2] = au_readl(MEM_STADDR2);
	sleep_static_memctlr[3][0] = au_readl(MEM_STCFG3);
	sleep_static_memctlr[3][1] = au_readl(MEM_STTIME3);
	sleep_static_memctlr[3][2] = au_readl(MEM_STADDR3);
}

161
static void restore_core_regs(void)
L
Linus Torvalds 已提交
162
{
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	/* 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();
L
Linus Torvalds 已提交
187

188 189 190 191 192 193
	/* 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
L
Linus Torvalds 已提交
194

195
	/* Restore the static memory controller configuration. */
L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208
	au_writel(sleep_static_memctlr[0][0], MEM_STCFG0);
	au_writel(sleep_static_memctlr[0][1], MEM_STTIME0);
	au_writel(sleep_static_memctlr[0][2], MEM_STADDR0);
	au_writel(sleep_static_memctlr[1][0], MEM_STCFG1);
	au_writel(sleep_static_memctlr[1][1], MEM_STTIME1);
	au_writel(sleep_static_memctlr[1][2], MEM_STADDR1);
	au_writel(sleep_static_memctlr[2][0], MEM_STCFG2);
	au_writel(sleep_static_memctlr[2][1], MEM_STTIME2);
	au_writel(sleep_static_memctlr[2][2], MEM_STADDR2);
	au_writel(sleep_static_memctlr[3][0], MEM_STCFG3);
	au_writel(sleep_static_memctlr[3][1], MEM_STTIME3);
	au_writel(sleep_static_memctlr[3][2], MEM_STADDR3);

209 210
	/*
	 * Enable the UART if it was enabled before sleep.
L
Linus Torvalds 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
	 * I guess I should define module control bits........
	 */
	if (sleep_uart0_enable & 0x02) {
		au_writel(0, UART0_ADDR + UART_MOD_CNTRL); au_sync();
		au_writel(1, UART0_ADDR + UART_MOD_CNTRL); au_sync();
		au_writel(3, UART0_ADDR + UART_MOD_CNTRL); au_sync();
		au_writel(sleep_uart0_inten, UART0_ADDR + UART_IER); au_sync();
		au_writel(sleep_uart0_fifoctl, UART0_ADDR + UART_FCR); au_sync();
		au_writel(sleep_uart0_linectl, UART0_ADDR + UART_LCR); au_sync();
		au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync();
	}

	restore_au1xxx_intctl();
}

unsigned long suspend_mode;

void wakeup_from_suspend(void)
{
	suspend_mode = 0;
}

233 234 235 236 237 238 239 240 241
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)
L
Linus Torvalds 已提交
242 243
{
	unsigned long wakeup, flags;
244 245 246 247 248
	int ret;
#ifdef SLEEP_TEST_TIMEOUT
#define TMPBUFLEN2 16
	char buf[TMPBUFLEN2], *p;
#endif
L
Linus Torvalds 已提交
249

250
	spin_lock_irqsave(&pm_lock, flags);
L
Linus Torvalds 已提交
251

252 253 254 255 256
	if (!write) {
		*len = 0;
		ret = 0;
		goto out_unlock;
	};
L
Linus Torvalds 已提交
257

258 259 260 261 262 263 264 265 266 267 268 269 270 271
#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
L
Linus Torvalds 已提交
272

273 274
	/**
	 ** The code below is all system dependent and we should probably
L
Linus Torvalds 已提交
275 276 277 278 279 280 281 282
	 ** have a function call out of here to set this up.  You need
	 ** to configure the GPIO or timer interrupts that will bring
	 ** you out of sleep.
	 ** For testing, the TOY counter wakeup is useful.
	 **/
#if 0
	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);

283
	/* GPIO 6 can cause a wake up event */
L
Linus Torvalds 已提交
284 285
	wakeup = au_readl(SYS_WAKEMSK);
	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
286
	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
L
Linus Torvalds 已提交
287
#else
288
	/* For testing, allow match20 to wake us up. */
L
Linus Torvalds 已提交
289 290 291 292 293 294 295 296
	wakeup = 1 << 8;	/* turn on match20 wakeup   */
	wakeup = 0;
#endif
	au_writel(1, SYS_WAKESRC);	/* clear cause */
	au_sync();
	au_writel(wakeup, SYS_WAKEMSK);
	au_sync();

297 298
	au_sleep();
	ret = 0;
L
Linus Torvalds 已提交
299

300
out_unlock:
L
Linus Torvalds 已提交
301
	spin_unlock_irqrestore(&pm_lock, flags);
302
	return ret;
L
Linus Torvalds 已提交
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318
#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)
L
Linus Torvalds 已提交
319
{
320 321
	unsigned long ticks, loopbit;
	int lps_precision = LPS_PREC;
L
Linus Torvalds 已提交
322

323
	loops_per_jiffy = 1 << 12;
L
Linus Torvalds 已提交
324

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
	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;
L
Linus Torvalds 已提交
352 353 354
	}
}

355 356
static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
		      void __user *buffer, size_t *len, loff_t *ppos)
L
Linus Torvalds 已提交
357 358 359 360 361 362 363
{
	int retval = 0, i;
	unsigned long val, pll;
#define TMPBUFLEN 64
#define MAX_CPU_FREQ 396
	char buf[TMPBUFLEN], *p;
	unsigned long flags, intc0_mask, intc1_mask;
364
	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
L
Linus Torvalds 已提交
365
	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
366
	unsigned long baud_rate;
L
Linus Torvalds 已提交
367 368

	spin_lock_irqsave(&pm_lock, flags);
369
	if (!write)
L
Linus Torvalds 已提交
370
		*len = 0;
371
	else {
L
Linus Torvalds 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
		/* Parse the new frequency */
		if (*len > TMPBUFLEN - 1) {
			spin_unlock_irqrestore(&pm_lock, flags);
			return -EFAULT;
		}
		if (copy_from_user(buf, buffer, *len)) {
			spin_unlock_irqrestore(&pm_lock, flags);
			return -EFAULT;
		}
		buf[*len] = 0;
		p = buf;
		val = simple_strtoul(p, &p, 0);
		if (val > MAX_CPU_FREQ) {
			spin_unlock_irqrestore(&pm_lock, flags);
			return -EFAULT;
		}

		pll = val / 12;
		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
391
			/* Revisit this for higher speed CPUs */
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399
			spin_unlock_irqrestore(&pm_lock, flags);
			return -EFAULT;
		}

		old_baud_base = get_au1x00_uart_baud_base();
		old_cpu_freq = get_au1x00_speed();

		new_cpu_freq = pll * 12 * 1000000;
400 401
	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
							    & 0x03) + 2) * 16));
L
Linus Torvalds 已提交
402 403 404 405
		set_au1x00_speed(new_cpu_freq);
		set_au1x00_uart_baud_base(new_baud_base);

		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
406 407
		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
L
Linus Torvalds 已提交
408 409 410 411 412 413

		au_writel(pll, SYS_CPUPLL);
		au_sync_delay(1);
		au_writel(new_refresh, MEM_SDREFCFG);
		au_sync_delay(1);

414 415 416 417 418
		for (i = 0; i < 4; i++)
			if (au_readl(UART_BASE + UART_MOD_CNTRL +
				     i * 0x00100000) == 3) {
				old_clk = au_readl(UART_BASE + UART_CLK +
						   i * 0x00100000);
L
Linus Torvalds 已提交
419
				baud_rate = old_baud_base / old_clk;
420 421
				/*
				 * We won't get an exact baud rate and the error
L
Linus Torvalds 已提交
422 423 424 425 426 427 428 429 430 431 432 433 434 435
				 * could be significant enough that our new
				 * calculation will result in a clock that will
				 * give us a baud rate that's too far off from
				 * what we really want.
				 */
				if (baud_rate > 100000)
					baud_rate = 115200;
				else if (baud_rate > 50000)
					baud_rate = 57600;
				else if (baud_rate > 30000)
					baud_rate = 38400;
				else if (baud_rate > 17000)
					baud_rate = 19200;
				else
436
					baud_rate = 9600;
L
Linus Torvalds 已提交
437
				new_clk = new_baud_base / baud_rate;
438 439
				au_writel(new_clk, UART_BASE + UART_CLK +
					  i * 0x00100000);
L
Linus Torvalds 已提交
440 441 442 443
				au_sync_delay(10);
			}
	}

R
Ralf Baechle 已提交
444 445 446
	/*
	 * We don't want _any_ interrupts other than match20. Otherwise our
	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
L
Linus Torvalds 已提交
447 448 449
	 */
	intc0_mask = save_local_and_disable(0);
	intc1_mask = save_local_and_disable(1);
450 451 452 453
	val = 1 << (AU1000_TOY_MATCH2_INT - AU1000_INTC0_INT_BASE);
	au_writel(val, IC0_MASKSET);	/* unmask */
	au_writel(val, IC0_WAKESET);	/* enable wake-from-sleep */
	au_sync();
L
Linus Torvalds 已提交
454
	spin_unlock_irqrestore(&pm_lock, flags);
P
Pete Popov 已提交
455
	au1000_calibrate_delay();
L
Linus Torvalds 已提交
456 457
	restore_local_and_enable(0, intc0_mask);
	restore_local_and_enable(1, intc1_mask);
R
Ralf Baechle 已提交
458

L
Linus Torvalds 已提交
459 460
	return retval;
}
461
#endif
L
Linus Torvalds 已提交
462 463

static struct ctl_table pm_table[] = {
464 465 466 467 468 469 470 471
	{
		.ctl_name	= CTL_UNNUMBERED,
		.procname	= "sleep",
		.data		= NULL,
		.maxlen		= 0,
		.mode		= 0600,
		.proc_handler	= &pm_do_sleep
	},
472
#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
473 474 475 476 477 478 479 480
	{
		.ctl_name	= CTL_UNNUMBERED,
		.procname	= "freq",
		.data		= NULL,
		.maxlen		= 0,
		.mode		= 0600,
		.proc_handler	= &pm_do_freq
	},
481
#endif
482
	{}
L
Linus Torvalds 已提交
483 484 485
};

static struct ctl_table pm_dir_table[] = {
486 487 488 489 490 491 492
	{
		.ctl_name	= CTL_UNNUMBERED,
		.procname	= "pm",
		.mode		= 0555,
		.child		= pm_table
	},
	{}
L
Linus Torvalds 已提交
493 494 495 496 497 498 499
};

/*
 * Initialize power interface
 */
static int __init pm_init(void)
{
500 501 502 503 504 505 506 507 508
	/* init TOY to tick at 1Hz. No need to wait for access bits
	 * since there's plenty of time between here and the first
	 * suspend cycle.
	 */
	if (au_readl(SYS_TOYTRIM) != 32767) {
		au_writel(32767, SYS_TOYTRIM);
		au_sync();
	}

509
	register_sysctl_table(pm_dir_table);
L
Linus Torvalds 已提交
510 511 512 513 514
	return 0;
}

__initcall(pm_init);

515
#endif	/* CONFIG_PM */