162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2001 Keith M Wesolowski 762306a36Sopenharmony_ci * Copyright (C) 2001 Paul Mundt 862306a36Sopenharmony_ci * Copyright (C) 2003 Guido Guenther <agx@sigxcpu.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/compiler.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/panic_notifier.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci#include <linux/notifier.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/rtc/ds1685.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/pm.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/addrspace.h> 2562306a36Sopenharmony_ci#include <asm/irq.h> 2662306a36Sopenharmony_ci#include <asm/reboot.h> 2762306a36Sopenharmony_ci#include <asm/wbflush.h> 2862306a36Sopenharmony_ci#include <asm/ip32/mace.h> 2962306a36Sopenharmony_ci#include <asm/ip32/crime.h> 3062306a36Sopenharmony_ci#include <asm/ip32/ip32_ints.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define POWERDOWN_TIMEOUT 120 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Blink frequency during reboot grace period and when panicked. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#define POWERDOWN_FREQ (HZ / 4) 3762306a36Sopenharmony_ci#define PANIC_FREQ (HZ / 8) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciextern struct platform_device ip32_rtc_device; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic struct timer_list power_timer, blink_timer; 4262306a36Sopenharmony_cistatic unsigned long blink_timer_timeout; 4362306a36Sopenharmony_cistatic int has_panicked, shutting_down; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic __noreturn void ip32_poweroff(void *data) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci void (*poweroff_func)(struct platform_device *) = 4862306a36Sopenharmony_ci symbol_get(ds1685_rtc_poweroff); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#ifdef CONFIG_MODULES 5162306a36Sopenharmony_ci /* If the first __symbol_get failed, our module wasn't loaded. */ 5262306a36Sopenharmony_ci if (!poweroff_func) { 5362306a36Sopenharmony_ci request_module("rtc-ds1685"); 5462306a36Sopenharmony_ci poweroff_func = symbol_get(ds1685_rtc_poweroff); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!poweroff_func) 5962306a36Sopenharmony_ci pr_emerg("RTC not available for power-off. Spinning forever ...\n"); 6062306a36Sopenharmony_ci else { 6162306a36Sopenharmony_ci (*poweroff_func)((struct platform_device *)data); 6262306a36Sopenharmony_ci symbol_put(ds1685_rtc_poweroff); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci unreachable(); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void ip32_machine_restart(char *cmd) __noreturn; 6962306a36Sopenharmony_cistatic void ip32_machine_restart(char *cmd) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci msleep(20); 7262306a36Sopenharmony_ci crime->control = CRIME_CONTROL_HARD_RESET; 7362306a36Sopenharmony_ci unreachable(); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void blink_timeout(struct timer_list *unused) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned long led = mace->perif.ctrl.misc ^ MACEISA_LED_RED; 7962306a36Sopenharmony_ci mace->perif.ctrl.misc = led; 8062306a36Sopenharmony_ci mod_timer(&blink_timer, jiffies + blink_timer_timeout); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void ip32_machine_halt(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci ip32_poweroff(&ip32_rtc_device); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void power_timeout(struct timer_list *unused) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci ip32_poweroff(&ip32_rtc_device); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid ip32_prepare_poweroff(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci if (has_panicked) 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (shutting_down || kill_cad_pid(SIGINT, 1)) { 9962306a36Sopenharmony_ci /* No init process or button pressed twice. */ 10062306a36Sopenharmony_ci ip32_poweroff(&ip32_rtc_device); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci shutting_down = 1; 10462306a36Sopenharmony_ci blink_timer_timeout = POWERDOWN_FREQ; 10562306a36Sopenharmony_ci blink_timeout(&blink_timer); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci timer_setup(&power_timer, power_timeout, 0); 10862306a36Sopenharmony_ci power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ; 10962306a36Sopenharmony_ci add_timer(&power_timer); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int panic_event(struct notifier_block *this, unsigned long event, 11362306a36Sopenharmony_ci void *ptr) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci unsigned long led; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (has_panicked) 11862306a36Sopenharmony_ci return NOTIFY_DONE; 11962306a36Sopenharmony_ci has_panicked = 1; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* turn off the green LED */ 12262306a36Sopenharmony_ci led = mace->perif.ctrl.misc | MACEISA_LED_GREEN; 12362306a36Sopenharmony_ci mace->perif.ctrl.misc = led; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci blink_timer_timeout = PANIC_FREQ; 12662306a36Sopenharmony_ci blink_timeout(&blink_timer); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return NOTIFY_DONE; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct notifier_block panic_block = { 13262306a36Sopenharmony_ci .notifier_call = panic_event, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic __init int ip32_reboot_setup(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci /* turn on the green led only */ 13862306a36Sopenharmony_ci unsigned long led = mace->perif.ctrl.misc; 13962306a36Sopenharmony_ci led |= MACEISA_LED_RED; 14062306a36Sopenharmony_ci led &= ~MACEISA_LED_GREEN; 14162306a36Sopenharmony_ci mace->perif.ctrl.misc = led; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci _machine_restart = ip32_machine_restart; 14462306a36Sopenharmony_ci _machine_halt = ip32_machine_halt; 14562306a36Sopenharmony_ci pm_power_off = ip32_machine_halt; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci timer_setup(&blink_timer, blink_timeout, 0); 14862306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &panic_block); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cisubsys_initcall(ip32_reboot_setup); 154