356 lines
8.2 KiB
C
356 lines
8.2 KiB
C
/*
|
|
* 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 <rthw.h>
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
#include <finsh.h>
|
|
|
|
#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();
|
|
}
|