/*--------------------------------------------------------------------------------*\ * * 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 (AR934X_SRAM_SIZE_HBEE / 2) #define ATH_REBOOT_LOGBUFFER (AR934X_SRAM_BASE_UNCACHED + AR934X_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 ((AR934X_SRAM_SIZE / 2) + (AR934X_SRAM_SIZE / 4)) #define ATH_REBOOT_LOGBUFFER (AR934X_SRAM_BASE_UNCACHED + AR934X_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; ktime_get_ts(&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) ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ 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); } /*------------------------------------------------------------------------------------------*\ * Set MDIO Boot Register to a specific value to inform Host about status * \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_SOC_AR934X) || defined(CONFIG_SOC_QCA955X) void ath79_set_boot_mdio_regs(unsigned int num_vals, uint16_t *vals) { int i; uint32_t *mdio_reg = (uint32_t *) KSEG1ADDR(AR934X_MDIO_BOOT_REG); if (num_vals > 8) num_vals = 8; for (i = 0; i < num_vals; i++) mdio_reg[i] = vals[i] & 0x0000ffff; for (; i < 8; i++) mdio_reg[i] = 0x00000000; } #endif /* defined(CONFIG_SOC_AR934X) || defined(CONFIG_SOC_QCA955X) */