--- uClinux-2.4.x.orig/arch/m68knommu/config.in Tue Apr 2 23:12:20 2002 +++ linux-2.4.x.orig.patched/arch/m68knommu/config.in Wed Mar 27 23:39:16 2002 @@ -293,6 +286,12 @@ bool ' ACPI interpreter (EXPERIMENTAL)' CONFIG_ACPI_INTERPRETER bool ' Enter S1 for sleep (EXPERIMENTAL)' CONFIG_ACPI_S1_SLEEP fi +fi + +if [ "$CONFIG_UCSIMM" = "y" -o "$CONFIG_UCDIMM" = "y"]; then + dep_bool ' Doze sleep mode only (clocks running)' CONFIG_PM_DOZE_ONLY $CONFIG_PM + dep_bool ' Wake up on power button (IRQ3)' CONFIG_PM_POWER_BUTTON_IRQ3 $CONFIG_PM + dep_bool ' Support user level pm_helper (/sbin/pm_helper)' CONFIG_PM_HELPER $CONFIG_PM fi dep_tristate ' Advanced Power Management BIOS support' CONFIG_APM $CONFIG_PM diff -Naur uClinux-2.4.x.orig/arch/m68knommu/platform/68328/pm.c linux-2.4.x.orig.patched/arch/m68knommu/platform/68328/pm.c --- uClinux-2.4.x.orig/arch/m68knommu/platform/68328/pm.c Thu Jan 1 10:00:00 1970 +++ linux-2.4.x.orig.patched/arch/m68knommu/platform/68328/pm.c Sun Apr 7 10:15:13 2002 @@ -0,0 +1,240 @@ +/* + * 68328 Power Management Routines + * + * Copyright 2002 Daniel Potts + * Embedded Systems, Co., LTD, Korea + * + * 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. + */ + +/* + * Debug macros + */ +#define DEBUG 1 +#ifdef DEBUG +# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + + +#include +#include +#include +#include +#include + +#include /* WARNING: included for CTL_ACPI and ACPI_S1_SLP_TYP */ + +#ifdef CONFIG_M68328 +#include +#endif + +#ifdef CONFIG_M68EZ328 +#include +#endif + +#ifdef CONFIG_M68VZ328 +#include +#endif + +#include +#include /* for interrupt stuff (that shouldn't be here)*/ +#include + +static unsigned long sleep_irq_mask = 0L; /* irqs to enable while in sleep mode */ + +#if defined (CONFIG_68328_SERIAL) +void shutdown_console(void); +void startup_console(void); +#endif + +void set_sleep_mask(unsigned long mask) +{ + sleep_irq_mask = mask; +} + +static int cpu_suspend(void) +{ + unsigned long flags; + unsigned long imr_flags; + + save_flags(flags); + cli(); + imr_flags = IMR; + +#if defined (CONFIG_68328_SERIAL) + /* Console is shutdown here as a special device, + * to ensure that the kernel will not hang trying to write to console. + */ + shutdown_console(); +#endif + + /* write out sleep enabled interrupts */ + IMR = ~(sleep_irq_mask); + +#if defined (CONFIG_PM_DOZE_ONLY) + PCTRL = PCTRL_PCEN; +#else + PLLCR |= PLLCR_DISPLL; + __asm__("stop #0x2000" : : : "cc"); +#endif + + /* Resume normal operation */ + +#if defined (CONFIG_68328_SERIAL) + startup_console(); +#endif + + IMR = imr_flags; + restore_flags(flags); + + return 0; +} + +#if defined (CONFIG_PM_HELPER) + +static char pm_helper_path[128] = "/sbin/pm_helper"; + +static void run_pm_helper(pm_request_t action) +{ + int r; + char *argv[3], *envp[3]; + + if (!pm_helper_path[0]) + return; + if (!current->fs->root) + return; + if (action != PM_SUSPEND && action != PM_RESUME) + return; + + argv[0] = pm_helper_path; + argv[1] = (action == PM_RESUME ? "resume" : "suspend"); + argv[2] = 0; + + /* minimal command environment */ + envp[0] = "HOME=/tmp"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[2] = 0; + + r = call_usermodehelper(argv[0], argv, envp); + if (r != 0) + printk("pm: user mode pm_helper returned %d\n", r); +} + +/* + * pm_suggest_suspend() + * + * This gets called elsewhere in the kernel when the system should be placed + * into suspend mode. For example, a power button handler. + * It calls the user level power management handler. + */ +int pm_suggest_suspend(void) +{ + run_pm_helper(PM_SUSPEND); + return 0; +} +#endif + + +#if defined (CONFIG_SYSCTL) +static int sysctl_pm_do_suspend(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int r; + + *lenp = 0; + + DPRINTK("System attempting to suspend.\n"); + r = pm_send_all(PM_SUSPEND, (void *)2); + if (r) { + return r; + } + + r = cpu_suspend(); + + pm_send_all(PM_RESUME, (void *)0); +#if defined (CONFIG_PM_HELPER) + run_pm_helper(PM_RESUME); +#endif + + DPRINTK("System resumed normal operation.\n"); + return r; +} + + +static struct ctl_table pm_table[] = +{ + {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_do_suspend}, +#if defined (CONFIG_PM_HELPER) + {2, "pm_helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring}, +#endif + {0} +}; + +static struct ctl_table pm_dir_table[] = +{ + {CTL_ACPI, "pm", NULL, 0, 0555, pm_table}, + {0} +}; + + +/* + * Initialize power interface + */ +static int __init pm_init(void) +{ + printk("Power management driver for 68328, Daniel Potts \n"); + + register_sysctl_table(pm_dir_table, 1); + return 0; +} + +__initcall(pm_init); + +#endif /* CONFIG_SYSCTL */ + + +#if defined (CONFIG_PM_POWER_BUTTON_IRQ3) +/* EXAMPLE power button handler: + * Here we use IRQ3 as our wake up from sleep mode interrupt + */ +static void power_button_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + ISR |= ISR_IRQ3; /* ack edge interrupt */ + + restore_flags(flags); +} + +static int __init power_button(void) +{ + int err; + + /* init interrupt section */ + PDDIR &= ~PD_IRQ3; + PDSEL &= ~PD_IRQ3; + + ICR |= ICR_ET3; /* edge sensitive */ + ISR |= ISR_IRQ3; /* clear it */ + + set_sleep_mask(IMR_MIRQ3); + + err = request_irq(IRQ3_IRQ_NUM, power_button_irq, + IRQ_FLG_STD, "power button", NULL); + + if(err) + printk("power irq failed\n"); + return 0; +} + +__initcall(power_button); + +#endif /* CONFIG_PM_POWER_BUTTON_IRQ3 */ --- uClinux-2.4.x.orig/arch/m68knommu/platform/68EZ328/Makefile Sun Mar 3 00:46:45 2002 +++ linux-2.4.x.orig.patched/arch/m68knommu/platform/68EZ328/Makefile Wed Mar 27 23:16:05 2002 @@ -17,6 +17,8 @@ O_TARGET := platform.o obj-y := entry.o config.o signal.o traps.o ints.o +obj-$(CONFIG_PM) += pm.o + $(BOARD)/crt0_$(MODEL).o: $(BOARD)/crt0_$(MODEL).S $(BOARD)/bootlogo.rh entry.o: entry.S m68k_defs.h --- uClinux-2.4.x.orig/arch/m68knommu/platform/68VZ328/Makefile Sun Mar 3 12:33:33 2002 +++ linux-2.4.x.orig.patched/arch/m68knommu/platform/68VZ328/Makefile Wed Mar 27 23:16:01 2002 @@ -17,6 +17,8 @@ O_TARGET := platform.o obj-y := entry.o config.o signal.o traps.o ints.o +obj-$(CONFIG_PM) += pm.o + $(BOARD)/crt0_$(MODEL).o: $(BOARD)/crt0_$(MODEL).S $(BOARD)/crt0_fixed.S $(BOARD)/bootlogo.rh entry.o: entry.S m68k_defs.h --- uClinux-2.4.x.orig/arch/m68knommu/platform/68VZ328/ucdimm/ints.c Tue Mar 26 20:33:12 2002 +++ linux-2.4.x.orig.patched/arch/m68knommu/platform/68VZ328/ucdimm/ints.c Wed Mar 27 23:35:13 2002 @@ -351,3 +351,10 @@ mach_get_irq_list = M68VZ328_get_irq_list; mach_process_int = M68VZ328_do_irq; } + + +void init_irq_proc(void); +void init_irq_proc(void) +{ + /* Insert /proc/irq driver here */ +} --- uClinux-2.4.x.orig/drivers/char/68328serial.c Sun Mar 3 12:34:03 2002 +++ linux-2.4.x.orig.patched/drivers/char/68328serial.c Sun Apr 7 10:32:27 2002 @@ -7,6 +7,7 @@ * * VZ Support/Fixes Evan Stawnyczy * Multiple UART support Daniel Potts + * Power management support Daniel Potts */ #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -1444,7 +1446,58 @@ printk("MC68328 serial driver version 1.00\n"); } -volatile int test_done; +#ifdef CONFIG_PM +/* Serial Power management + * The console (currently fixed at line 0) is a special case for power + * management because the kernel is so chatty. The console will be + * explicitly disabled my our power manager as the last minute, so we won't + * mess with it here. + */ +static struct pm_dev *serial_pm[NR_PORTS]; + +static int serial_pm_callback(struct pm_dev *dev, pm_request_t request, void *data) +{ + struct m68k_serial *info = (struct m68k_serial *)dev->data; + + if(info == NULL) + return -1; + + /* special case for line 0 - pm restores it */ + if(info->line == 0) + return 0; + + switch (request) { + case PM_SUSPEND: + shutdown(info); + break; + + case PM_RESUME: + startup(info); + break; + } + return 0; +} + +void shutdown_console(void) +{ + struct m68k_serial *info = &m68k_soft[0]; + + /* HACK: wait a bit for any pending printk's to be dumped */ + { + int i = 10000; + while(i--); + } + + shutdown(info); +} + +void startup_console(void) +{ + struct m68k_serial *info = &m68k_soft[0]; + startup(info); +} +#endif + /* rs_init inits the driver */ static int __init @@ -1545,10 +1598,17 @@ IRQ_FLG_STD, "M68328_UART", NULL)) panic("Unable to attach 68328 serial interrupt\n"); +#ifdef CONFIG_PM + serial_pm[i] = pm_register(PM_SYS_DEV, PM_SYS_COM, serial_pm_callback); + if (serial_pm[i]) + serial_pm[i]->data = info; +#endif } restore_flags(flags); return 0; } + + /* * register_serial and unregister_serial allows for serial ports to be --- uClinux-2.4.x.orig/drivers/net/cs89x0.c Tue Mar 26 20:33:40 2002 +++ linux-2.4.x.orig.patched/drivers/net/cs89x0.c Sun Apr 7 10:26:11 2002 @@ -87,6 +87,10 @@ : Customized for use on uClinux & MC68EZ328 platforms. Evan Stawnyczy : Customized for use on MC68VZ328 platform. + + Daniel Potts : uClinux sleep support, ucDimm + Mark McChrystal : uClinux sleep support, ucSimm + */ /* Always include 'config.h' first in case the user wants to turn on @@ -150,6 +154,7 @@ #include #include #include +#include #include "cs89x0.h" @@ -287,7 +292,11 @@ __setup("cs89x0_dma=", dma_fn); #endif /* !defined(MODULE) && (ALLOW_DMA != 0) */ - + +#ifdef CONFIG_PM +static int cs89x0_in_use = 0; +#endif + /* Check for a network adaptor of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. @@ -407,6 +416,53 @@ #endif /* NO_EPROM */ +#if defined(CONFIG_PM) + +static struct pm_dev *cs89x0_pm; + +/* cs89x0_pm_callback(): + * + * We don't actually suspend or resume here - that is left to the + * net_open/close functions. + * On PM_SUSPEND: we check that the connection is closed down. If it is, we + * return success. PM_RESUME does nothing. + * It is required that the user brings the connection down (perhaps via + * a pm helper daemon) + */ +static int cs89x0_pm_callback(struct pm_dev *dev, pm_request_t request, void *data) +{ + struct net_device *cs_dev = (struct net_device *)dev->data; + + switch (request) { + case PM_SUSPEND: + if (cs89x0_in_use) { + printk("cs89x00 connection still open, not sleeping\n"); + return -EBUSY; + } + +#if defined(CONFIG_UCSIMM) + /* set sleep pin low */ + *(volatile unsigned char *)0xfffff429 &= ~(0x01); + writereg(cs_dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) & ~(AUTO_WAKEUP)); +#elif defined(CONFIG_UCDIMM) + *(volatile unsigned char *)0xfffff431 &= ~(0x08); +#endif + writereg(cs_dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | SLEEP_ON); + break; + case PM_RESUME: +#if defined (CONFIG_UCSIMM) + *(volatile unsigned short *)0xfffff429 |= 0x01; /* not sleeping */ +#elif defined(CONFIG_UCDIMM) + *(volatile unsigned char *)0xfffff431 |= (0x08); +#endif + break; + } + + return 0; +} + +#endif + /* This is the real probe routine. Linux has a history of friendly device probes on the ISA bus. A good device probes avoids doing writes, and verifies that the correct device exists and functions. @@ -426,14 +482,19 @@ #if defined( CONFIG_UCSIMM ) || defined(CONFIG_UCDIMM) #ifdef CONFIG_UCSIMM printk("cs89x0: Setting up uCsimm CS8900 Chip Select & IRQ ioaddr = 0x%X\n",ioaddr); -#else - printk("cs89x0: Setting up uCcs8900 Chip Select & IRQ ioaddr = 0x%X\n",ioaddr); -#endif /* set up the chip select */ *(volatile unsigned char *)0xfffff42b |= 0x01; /* output /sleep */ *(volatile unsigned short *)0xfffff428 |= 0x0101; /* not sleeping */ +#else /* UCDIMM */ + printk("cs89x0: Setting up uCcs8900 Chip Select & IRQ ioaddr = 0x%X\n",ioaddr); + + /* set up the chip select */ + *(volatile unsigned char *)0xfffff430 |= 0x08; + *(volatile unsigned char *)0xfffff433 |= 0x08; + *(volatile unsigned char *)0xfffff431 |= (0x08); /* sleep */ +#endif *(volatile unsigned char *)0xfffff42b &= ~0x02; /* input irq5 */ *(volatile unsigned short *)0xfffff428 &= ~0x0202; /* irq5 fcn on */ @@ -734,6 +795,11 @@ printk("\n"); if (net_debug) printk("cs89x0_probe1() successful\n"); +#if defined (CONFIG_PM) + cs89x0_pm = pm_register(PM_SYS_DEV, PM_SYS_COM, cs89x0_pm_callback); + if (cs89x0_pm) + cs89x0_pm->data = dev; +#endif return 0; out2: release_region(ioaddr & ~3, NETCARD_IO_EXTENT); @@ -1383,6 +1449,9 @@ netif_start_queue(dev); if (net_debug > 1) printk("cs89x0: net_open() succeeded\n"); +#ifdef CONFIG_PM + cs89x0_in_use = 1; +#endif return 0; bad_out: return ret; @@ -1633,6 +1702,10 @@ free_dma(dev->dma); release_dma_buff(lp); } +#endif + +#if defined(CONFIG_PM) + cs89x0_in_use = 0; #endif /* Update the statistics here. */