/*--------------------------------------------------------------------------------*\ * * Copyright (C) 2013 AVM GmbH * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * * * Log printk-buffer and Reboot-Status to internal SRAM (wasp) * all other atheros-chipsets we use as small DRAM-Area to save Reboot-Status * * Author: mbahr@avm.de \*--------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _reboot_info { enum _avm_reset_status status; char *printouttext; }; static const struct _reboot_info reboot_info[] = { { status: RS_POWERON, printouttext: "Power-On" }, { status: RS_SOFTWATCHDOG, printouttext: "Softwatchdog-Reboot" }, { status: RS_NMIWATCHDOG, printouttext: "NMI-Watchdog-Reset" }, { status: RS_REBOOT, printouttext: "Soft-Reboot" }, { status: RS_FIRMWAREUPDATE, printouttext: "Fw-Update" }, { status: RS_NMI_WA, printouttext: "Fw-Update" }, { status: RS_SHORTREBOOT, printouttext: "Short-PowerOff-Reboot" }, { status: RS_BUSERROR, printouttext: "Bus-Error-Reboot" }, }; /*--- use internal sram, but not at the begin, because here maybe data from mni-workarround (aprox first 0x500 bytes) ---*/ #if defined(CONFIG_SOC_QCA953X) #define ATH_SRAM_PRINTKLEN (ATH_SRAM_SIZE_HBEE / 2) #define ATH_REBOOT_LOGBUFFER (ATH_SRAM_BASE_UNCACHED + ATH_SRAM_SIZE_HBEE - ATH_SRAM_PRINTKLEN) #elif defined(CONFIG_SOC_AR934X) /*--- use internal sram, but not at the begin, because here maybe data from mni-workarround (aprox first 0x500 bytes) ---*/ #define ATH_SRAM_PRINTKLEN ((ATH_SRAM_SIZE / 2) + (ATH_SRAM_SIZE / 4)) #define ATH_REBOOT_LOGBUFFER (ATH_SRAM_BASE_UNCACHED + ATH_SRAM_SIZE - ATH_SRAM_PRINTKLEN) #else/*--- #if defined(CONFIG_SOC_AR934X) ---*/ #undef ATH_SRAM_PRINTKLEN #define ATH_REBOOT_LOGBUFFER (0xA1000000 - 512) #endif/*--- #else ---*//*--- #if defined(CONFIG_SOC_AR934X) ---*/ #define MAGIC_PRINK_WORD 0x5352414D #define MAGIC_REBOOT_STATUS_WORD 0x53544130 #define MAGIC_REBOOT_STATUS_WORD_SET(a) (((a) & 0xF) | MAGIC_REBOOT_STATUS_WORD) #define MAGIC_REBOOT_STATUS_WORD_CHECK(a) ((((a) & ~0xF) == MAGIC_REBOOT_STATUS_WORD) && ((a) != MAGIC_REBOOT_STATUS_WORD_SET(RS_SHORTREBOOT))) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _ram_print_data { volatile unsigned int rebootstatus; volatile unsigned int magic_word; volatile unsigned int len; unsigned char data[1]; }; /*--------------------------------------------------------------------------------*\ * save log to internal SRAM \*--------------------------------------------------------------------------------*/ int tffs_fill_panic_log_buffer(char *p_log_buf, unsigned int maxsize); void ath_savelog_to_ram(void) { #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) char time_stamp_buf[sizeof("UPTIME: \n") + 10 + sizeof("SRAM LOG VERSION 1.0\n")]; unsigned long time_stamp_buf_len; struct timespec uptime; size_t towrite; extern unsigned long printk_get_buffer(char **p_log_buf, unsigned long *p_log_end, unsigned long *p_anzahl); struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; char *pdata; unsigned long limit; do_posix_clock_monotonic_gettime(&uptime); monotonic_to_bootbased(&uptime); time_stamp_buf_len = snprintf(time_stamp_buf, sizeof(time_stamp_buf), "UPTIME: %lu\nSRAM LOG VERSION 1.0\n", (unsigned long) uptime.tv_sec); printk_avm_console_bend(0); /* force serial-output */ psram_print->len = 0; psram_print->magic_word = MAGIC_PRINK_WORD; limit = ATH_SRAM_PRINTKLEN - sizeof(struct _ram_print_data); pdata = psram_print->data; memcpy(pdata, time_stamp_buf, time_stamp_buf_len); pdata += time_stamp_buf_len, limit -= time_stamp_buf_len; if(kmsg_dump_get_buffer_nocontext(0, pdata, limit, &towrite) == true) { psram_print->len = towrite; } else { psram_print->len = 0; } psram_print->len += time_stamp_buf_len; psram_print->magic_word = MAGIC_PRINK_WORD; #endif /*--- #ifdef CONFIG_TFFS_PANIC_LOG ---*/ } #define rprint(buf, buf_len, args...) if(buf) { \ if(buf_len > 0) { \ unsigned int slen = snprintf(buf, buf_len, args); \ buf += slen, buf_len -= slen; \ } \ } else { \ printk(KERN_ERR args);\ } /*--------------------------------------------------------------------------------*\ * Restore Log from internal SRAM (if exist) * txtbuf, txtlen: destination (if NULL use printk) * ret: Laenge \*--------------------------------------------------------------------------------*/ int ath_restore_log_from_ram(char *txtbuf, unsigned int txtlen) { char *txtstart = txtbuf; #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; char buf[128]; /*--- printk(KERN_INFO"%s: %p len=%d/%d %x\n", __func__, txtbuf, txtlen,psram_print->len, psram_print->magic_word); ---*/ if((psram_print->len < ATH_SRAM_PRINTKLEN) && (psram_print->magic_word == MAGIC_PRINK_WORD)) { char *p = psram_print->data; unsigned int idx = 0; unsigned int len = psram_print->len; /*--- rprint(txtbuf, txtlen, "--------------- LOG found in SRAM (len = %u) ------------------------\n", len); ---*/ while(len) { if(idx < (sizeof(buf) - 2)) { if((*p != '\r') && (*p != '\n')) { if(((*p >= 0x80) || (*p < 0x20)) && (*p != '\t')) { buf[idx] = '?'; } else { buf[idx] = *p; } p++, idx++, len--; continue; } p++, len--; } if(idx) { buf[idx] = 0; rprint(txtbuf, txtlen,"%s\n", buf); idx = 0; } } if(idx) { rprint(txtbuf, txtlen,"%s\n", buf); } /*--- rprint(txtbuf, txtlen,"---------------------------------------------------------------------\n"); ---*/ } #endif/*--- #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) ---*/ return txtbuf - txtstart; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void ath_inval_log_from_ram(void) { #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) struct _ram_print_data *psram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; psram_print->magic_word = 0; #endif/*--- #if defined(CONFIG_TFFS_PANIC_LOG) && defined(ATH_SRAM_PRINTKLEN) ---*/ } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void ath_save_rebootstatus_to_ram(unsigned int idx) { struct _ram_print_data *pram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; if(!MAGIC_REBOOT_STATUS_WORD_CHECK(pram_print->rebootstatus)) { pram_print->rebootstatus = MAGIC_REBOOT_STATUS_WORD_SET(idx); } } enum _avm_reset_status ath_reboot_status; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int ath_reboot_status_from_ram(void) { unsigned int i; struct _ram_print_data *pram_print = (struct _ram_print_data *)ATH_REBOOT_LOGBUFFER; ath_reboot_status = RS_POWERON; if(MAGIC_REBOOT_STATUS_WORD_CHECK(pram_print->rebootstatus)) { ath_reboot_status = pram_print->rebootstatus & 0xF; pram_print->rebootstatus = MAGIC_REBOOT_STATUS_WORD_SET(RS_SHORTREBOOT); } for(i = 0; i < ARRAY_SIZE(reboot_info); i++) { if(reboot_info[i].status == ath_reboot_status) { break; } } if(i >= ARRAY_SIZE(reboot_info)) { i = 0; } printk(KERN_ERR"(c) AVM 2014, Reboot Status is: %s\n", reboot_info[i].printouttext); /*--- ath_restore_log_from_ram(NULL, 0); ---*/ return 0; } arch_initcall(ath_reboot_status_from_ram); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ enum _avm_reset_status avm_reset_status(void){ return ath_reboot_status; } EXPORT_SYMBOL(avm_reset_status); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void avm_set_reset_status(enum _avm_reset_status status) { ath_save_rebootstatus_to_ram(status); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_NMI(void) { avm_set_reset_status(RS_NMIWATCHDOG); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_Update(void) { avm_set_reset_status(RS_FIRMWAREUPDATE); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_NMI_WA(void) { avm_set_reset_status(RS_NMI_WA); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_BusError(void) { avm_set_reset_status(RS_BUSERROR); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void set_reboot_status_to_SoftReboot(void) { avm_set_reset_status(RS_REBOOT); }