/* * File : drv_reset.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2016Äê7ÔÂ29ÈÕ Urey the first version */ #include #include #include #include #include "board.h" #include "drv_clock.h" #include "drv_ost.h" #include "drv_pmu.h" #include "drv_rtc.h" static void udelay(uint32_t x) { volatile uint32_t n = 1000; while(x--) { for (n = 0; n < 1000; ++n); } } static void mdelay(uint32_t x) { while(x--) udelay(1000); } #define RECOVERY_SIGNATURE (0x001a1a) #define REBOOT_SIGNATURE (0x003535) #define UNMSAK_SIGNATURE (0x7c0000)//do not use these bits void wdt_start_count(int msecs) { int time = BOARD_RTC_CLK / 64 * msecs / 1000; if(time > 65535) time = 65535; writel(1 << 16,TCU_BASE + TCU_TSCR); writel(0,WDT_BASE + WDT_TCNT); //counter writel(time,WDT_BASE + WDT_TDR); //data writel((3<<3 | 1<<1),WDT_BASE + WDT_TCSR); writel(0,WDT_BASE + WDT_TCER); writel(1,WDT_BASE + WDT_TCER); } void wdt_stop_count(void) { writel(1 << 16,TCU_BASE + TCU_TSCR); writel(0,WDT_BASE + WDT_TCNT); //counter writel(65535,WDT_BASE + WDT_TDR); //data writel(1 << 16,TCU_BASE + TCU_TSSR); } void wdt_clear(void) { writel(0,WDT_BASE + WDT_TCNT); } /* * Function: Keep power for CPU core when reset. * So that EPC, tcsm and so on can maintain it's status after reset-key pressed. */ static int inline reset_keep_power(void) { rtc_write_reg(RTC_BASE + RTC_PWRONCR, rtc_read_reg(RTC_BASE + RTC_PWRONCR) & ~(1 << 0)); return 0; } void x1000_hibernate(void) { uint32_t rtc_rtccr; rt_base_t level; wdt_stop_count(); level = rt_hw_interrupt_disable(); /* Set minimum wakeup_n pin low-level assertion time for wakeup: 1000ms */ rtc_write_reg(RTC_BASE + RTC_HWFCR, HWFCR_WAIT_TIME(1000)); /* Set reset pin low-level assertion time after wakeup: must > 60ms */ rtc_write_reg(RTC_BASE + RTC_HRCR, HRCR_WAIT_TIME(125)); /* clear wakeup status register */ rtc_write_reg(RTC_BASE + RTC_HWRSR, 0x0); rtc_write_reg(RTC_BASE + RTC_HWCR, 0x8); /* Put CPU to hibernate mode */ rtc_write_reg(RTC_BASE + RTC_HCR, 0x1); /*poweroff the pmu*/ // jz_notifier_call(NOTEFY_PROI_HIGH, JZ_POST_HIBERNATION, NULL); rtc_rtccr = rtc_read_reg(RTC_BASE + RTC_RTCCR); rtc_rtccr |= 0x1 << 0; rtc_write_reg(RTC_BASE + RTC_RTCCR,rtc_rtccr); mdelay(200); while(1) { rt_kprintf("%s:We should NOT come here.%08x\n",__func__, rtc_read_reg(RTC_BASE + RTC_HCR)); } } void x1000_wdt_restart(char *command) { rt_kprintf("Restarting after 4 ms\n"); if ((command != NULL) && !strcmp(command, "recovery")) { while (cpm_inl(CPM_CPPSR) != RECOVERY_SIGNATURE) { rt_kprintf("set RECOVERY_SIGNATURE\n"); cpm_outl(0x5a5a, CPM_CPSPPR); cpm_outl(RECOVERY_SIGNATURE, CPM_CPPSR); cpm_outl(0x0, CPM_CPSPPR); udelay(100); } } else { //WDT... cpm_outl(0x5a5a, CPM_CPSPPR); cpm_outl(REBOOT_SIGNATURE, CPM_CPPSR); cpm_outl(0x0, CPM_CPSPPR); } wdt_start_count(4); mdelay(200); while(1) rt_kprintf("check wdt.\n"); } void x1000_hibernate_restart(char *command) { rt_base_t level; uint32_t rtc_rtcsr,rtc_rtccr; level = rt_hw_interrupt_disable(); if ((command != NULL) && !strcmp(command, "recovery")) { x1000_wdt_restart(command); } /* hibernate_restart */ while(!(rtc_read_reg(RTC_BASE + RTC_RTCCR) & RTCCR_WRDY)); rtc_rtcsr = rtc_read_reg(RTC_BASE + RTC_RTCSR); rtc_rtccr = rtc_read_reg(RTC_BASE + RTC_RTCCR); rtc_write_reg(RTC_RTCSAR,rtc_rtcsr + 5); rtc_rtccr &= ~(1 << 4 | 1 << 1); rtc_rtccr |= 0x3 << 2; rtc_write_reg(RTC_BASE + RTC_RTCCR,rtc_rtccr); /* Clear reset status */ cpm_outl(0,CPM_RSR); /* Set minimum wakeup_n pin low-level assertion time for wakeup: 1000ms */ rtc_write_reg(RTC_BASE + RTC_HWFCR, HWFCR_WAIT_TIME(1000)); /* Set reset pin low-level assertion time after wakeup: must > 60ms */ rtc_write_reg(RTC_BASE + RTC_HRCR, HRCR_WAIT_TIME(125)); /* clear wakeup status register */ rtc_write_reg(RTC_BASE + RTC_HWRSR, 0x0); rtc_write_reg(RTC_BASE + RTC_HWCR, 0x9); /* Put CPU to hibernate mode */ rtc_write_reg(RTC_BASE + RTC_HCR, 0x1); rtc_rtccr = rtc_read_reg(RTC_BASE + RTC_RTCCR); rtc_rtccr |= 0x1 << 0; rtc_write_reg(RTC_BASE + RTC_RTCCR,rtc_rtccr); mdelay(200); while(1) rt_kprintf("%s:We should NOT come here.%08x\n",__func__, rtc_read_reg(RTC_BASE + RTC_HCR)); } uint32_t x1000_get_last_reset(void) { return (cpm_inl(CPM_RSR) & 0x0000000F); } /* ============================wdt control proc end =============================== */ /* ============================reset proc=================================== */ const char *reset_command[] = {"wdt","hibernate","recovery"}; #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) int x1000_reset(const char *reset_cmd) { rt_base_t level; int command_size = 0; int i; command_size = ARRAY_SIZE(reset_command); for (i = 0; i < command_size; i++) { if (!strncmp(reset_cmd, reset_command[i], strlen(reset_command[i]))) break; } if(i == command_size) return -RT_ERROR; level = rt_hw_interrupt_disable(); switch(i) { case 0: x1000_wdt_restart("wdt"); break; case 1: x1000_hibernate_restart("hibernate"); break; case 2: x1000_wdt_restart("recovery"); break; default: rt_kprintf("not support command %d\n", i); } rt_hw_interrupt_enable(level); return RT_EOK; } struct wdt_reset { int msecs; }; static struct wdt_reset _wdt_param = { .msecs = 1000, }; rt_err_t _wdt_init(rt_watchdog_t *wdt) { return RT_EOK; } rt_err_t _wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) { switch (cmd) { case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: { int msecs = *(int *)arg * 1000; if(msecs < 1000) msecs = 1000; if(msecs > 30000) msecs = 30000; _wdt_param.msecs = msecs; rt_kprintf("WDT timeout = %d\n",msecs); } break; case RT_DEVICE_CTRL_WDT_START: wdt_start_count(_wdt_param.msecs + 1000); break; case RT_DEVICE_CTRL_WDT_STOP: wdt_stop_count(); break; case RT_DEVICE_CTRL_WDT_KEEPALIVE: wdt_clear(); break; default: break; } return RT_EOK; } const struct rt_watchdog_ops _wdt_ops = { .init = _wdt_init, .control = _wdt_control }; static struct rt_watchdog_device _wdt_device = { .ops = (struct rt_watchdog_ops *)&_wdt_ops, }; int reboot(void) { rt_hw_cpu_reset(); return 0; } MSH_CMD_EXPORT(reboot,reboot system...); int shutdown(void) { rt_hw_cpu_shutdown(); } MSH_CMD_EXPORT(shutdown,shutdown system...); int rt_hw_wdt_init(void) { rt_hw_watchdog_register(&_wdt_device,"WDT",RT_DEVICE_FLAG_STANDALONE,&_wdt_param); return 0; } INIT_DEVICE_EXPORT(rt_hw_wdt_init); void rt_hw_cpu_reset() { /* Disable Base_board */ drv_pmu_power_down(); x1000_reset("wdt"); } void rt_hw_cpu_shutdown() { /* Disable Base_board */ drv_pmu_power_down(); x1000_hibernate(); }