| /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* System module for Chrome EC : LM4 hardware specific implementation */ |
| |
| #include "clock.h" |
| #include "common.h" |
| #include "console.h" |
| #include "cpu.h" |
| #include "host_command.h" |
| #include "panic.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| |
| /* Indices for hibernate data registers */ |
| enum hibdata_index { |
| HIBDATA_INDEX_SCRATCHPAD, /* General-purpose scratchpad */ |
| HIBDATA_INDEX_WAKE, /* Wake reasons for hibernate */ |
| HIBDATA_INDEX_SAVED_RESET_FLAGS, /* Saved reset flags */ |
| #ifdef CONFIG_SOFTWARE_PANIC |
| HIBDATA_INDEX_SAVED_PANIC_REASON, /* Saved panic reason */ |
| HIBDATA_INDEX_SAVED_PANIC_INFO, /* Saved panic data */ |
| HIBDATA_INDEX_SAVED_PANIC_EXCEPTION /* Saved panic exception code */ |
| #endif |
| }; |
| |
| /* Flags for HIBDATA_INDEX_WAKE */ |
| #define HIBDATA_WAKE_RTC (1 << 0) /* RTC alarm */ |
| #define HIBDATA_WAKE_HARD_RESET (1 << 1) /* Hard reset via short RTC alarm */ |
| #define HIBDATA_WAKE_PIN (1 << 2) /* Wake pin */ |
| |
| /* |
| * Time to hibernate to trigger a power-on reset. 50 ms is sufficient for the |
| * EC itself, but we need a longer delay to ensure the rest of the components |
| * on the same power rail are reset and 5VALW has dropped. |
| */ |
| #define HIB_RESET_USEC 1000000 |
| |
| /* |
| * Convert between microseconds and the hibernation module RTC subsecond |
| * register which has 15-bit resolution. Divide down both numerator and |
| * denominator to avoid integer overflow while keeping the math accurate. |
| */ |
| #define HIB_RTC_USEC_TO_SUBSEC(us) ((us) * (32768/64) / (1000000/64)) |
| #define HIB_RTC_SUBSEC_TO_USEC(ss) ((ss) * (1000000/64) / (32768/64)) |
| |
| /** |
| * Wait for a write to commit to a hibernate register. |
| * |
| * @return EC_SUCCESS if successful, non-zero if error. |
| */ |
| static int wait_for_hibctl_wc(void) |
| { |
| int i; |
| |
| /* Wait for write-capable */ |
| for (i = 0; i < 1000000; i++) { |
| if (LM4_HIBERNATE_HIBCTL & LM4_HIBCTL_WRC) |
| return EC_SUCCESS; |
| } |
| return EC_ERROR_TIMEOUT; |
| } |
| |
| /** |
| * Read hibernate register at specified index. |
| * |
| * @return The value of the register or 0 if invalid index. |
| */ |
| static uint32_t hibdata_read(enum hibdata_index index) |
| { |
| if (index < 0 || index >= LM4_HIBERNATE_HIBDATA_ENTRIES) |
| return 0; |
| |
| return LM4_HIBERNATE_HIBDATA[index]; |
| } |
| |
| /** |
| * Write hibernate register at specified index. |
| * |
| * @return nonzero if error. |
| */ |
| static int hibdata_write(enum hibdata_index index, uint32_t value) |
| { |
| int rv; |
| |
| if (index < 0 || index >= LM4_HIBERNATE_HIBDATA_ENTRIES) |
| return EC_ERROR_INVAL; |
| |
| /* Wait for ok-to-write */ |
| rv = wait_for_hibctl_wc(); |
| if (rv != EC_SUCCESS) |
| return rv; |
| |
| /* Write register */ |
| LM4_HIBERNATE_HIBDATA[index] = value; |
| |
| /* Wait for write-complete */ |
| return wait_for_hibctl_wc(); |
| } |
| |
| static void check_reset_cause(void) |
| { |
| uint32_t hib_status = LM4_HIBERNATE_HIBRIS; |
| uint32_t raw_reset_cause = LM4_SYSTEM_RESC; |
| uint32_t hib_wake_flags = hibdata_read(HIBDATA_INDEX_WAKE); |
| uint32_t flags = 0; |
| |
| /* Clear the reset causes now that we've read them */ |
| LM4_SYSTEM_RESC = 0; |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIC = hib_status; |
| hibdata_write(HIBDATA_INDEX_WAKE, 0); |
| |
| if (raw_reset_cause & 0x02) { |
| /* |
| * Full power-on reset of chip. This resets the flash |
| * protection registers to their permanently-stored values. |
| * Note that this is also triggered by hibernation, because |
| * that de-powers the chip. |
| */ |
| flags |= RESET_FLAG_POWER_ON; |
| } else if (!flags && (raw_reset_cause & 0x01)) { |
| /* |
| * LM4 signals the reset pin in RESC for all power-on resets, |
| * even though the external pin wasn't asserted. Make setting |
| * this flag mutually-exclusive with power on flag, so we can |
| * use it to indicate a keyboard-triggered reset. |
| */ |
| flags |= RESET_FLAG_RESET_PIN; |
| } |
| |
| if (raw_reset_cause & 0x04) |
| flags |= RESET_FLAG_BROWNOUT; |
| |
| if (raw_reset_cause & 0x10) |
| flags |= RESET_FLAG_SOFT; |
| |
| if (raw_reset_cause & 0x28) { |
| /* Watchdog timer 0 or 1 */ |
| flags |= RESET_FLAG_WATCHDOG; |
| } |
| |
| /* Handle other raw reset causes */ |
| if (raw_reset_cause && !flags) |
| flags |= RESET_FLAG_OTHER; |
| |
| |
| if ((hib_status & 0x09) && |
| (hib_wake_flags & HIBDATA_WAKE_HARD_RESET)) { |
| /* Hibernation caused by software-triggered hard reset */ |
| flags |= RESET_FLAG_HARD; |
| |
| /* Consume the hibernate reasons so we don't see them below */ |
| hib_status &= ~0x09; |
| } |
| |
| if ((hib_status & 0x01) && (hib_wake_flags & HIBDATA_WAKE_RTC)) |
| flags |= RESET_FLAG_RTC_ALARM; |
| |
| if ((hib_status & 0x08) && (hib_wake_flags & HIBDATA_WAKE_PIN)) |
| flags |= RESET_FLAG_WAKE_PIN; |
| |
| if (hib_status & 0x04) |
| flags |= RESET_FLAG_LOW_BATTERY; |
| |
| /* Restore then clear saved reset flags */ |
| flags |= hibdata_read(HIBDATA_INDEX_SAVED_RESET_FLAGS); |
| hibdata_write(HIBDATA_INDEX_SAVED_RESET_FLAGS, 0); |
| |
| system_set_reset_flags(flags); |
| } |
| |
| /* |
| * A3 and earlier chip stepping has a problem accessing flash during shutdown. |
| * To work around that, we jump to RAM before hibernating. This function must |
| * live in RAM. It must be called with interrupts disabled, cannot call other |
| * functions, and can't be declared static (or else the compiler optimizes it |
| * into the main hibernate function. |
| */ |
| void __attribute__((noinline)) __attribute__((section(".iram.text"))) |
| __enter_hibernate(int hibctl) |
| { |
| LM4_HIBERNATE_HIBCTL = hibctl; |
| while (1) |
| ; |
| } |
| |
| /** |
| * Read the real-time clock. |
| * |
| * @param ss_ptr Destination for sub-seconds value, if not null. |
| * |
| * @return the real-time clock seconds value. |
| */ |
| uint32_t system_get_rtc_sec_subsec(uint32_t *ss_ptr) |
| { |
| uint32_t rtc, rtc2; |
| uint32_t rtcss, rtcss2; |
| |
| /* |
| * The hibernate module isn't synchronized, so need to read repeatedly |
| * to guarantee a valid read. |
| */ |
| do { |
| rtc = LM4_HIBERNATE_HIBRTCC; |
| rtcss = LM4_HIBERNATE_HIBRTCSS & 0x7fff; |
| rtcss2 = LM4_HIBERNATE_HIBRTCSS & 0x7fff; |
| rtc2 = LM4_HIBERNATE_HIBRTCC; |
| } while (rtc != rtc2 || rtcss != rtcss2); |
| |
| if (ss_ptr) |
| *ss_ptr = rtcss; |
| |
| return rtc; |
| } |
| |
| timestamp_t system_get_rtc(void) |
| { |
| uint32_t rtc, rtc_ss; |
| timestamp_t time; |
| |
| rtc = system_get_rtc_sec_subsec(&rtc_ss); |
| |
| time.val = ((uint64_t)rtc) * SECOND + HIB_RTC_SUBSEC_TO_USEC(rtc_ss); |
| return time; |
| } |
| |
| /** |
| * Set the real-time clock. |
| * |
| * @param seconds New clock value. |
| */ |
| void system_set_rtc(uint32_t seconds) |
| { |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBRTCLD = seconds; |
| wait_for_hibctl_wc(); |
| } |
| |
| /** |
| * Set the hibernate RTC match time at a given time from now |
| * |
| * @param seconds Number of seconds from now for RTC match |
| * @param microseconds Number of microseconds from now for RTC match |
| */ |
| static void set_hibernate_rtc_match_time(uint32_t seconds, |
| uint32_t microseconds) |
| { |
| uint32_t rtc, rtcss; |
| |
| /* |
| * Make sure that the requested delay is not less then the |
| * amount of time it takes to set the RTC match registers, |
| * otherwise, the match event could be missed. |
| */ |
| if (seconds == 0 && microseconds < HIB_SET_RTC_MATCH_DELAY_USEC) |
| microseconds = HIB_SET_RTC_MATCH_DELAY_USEC; |
| |
| /* Calculate the wake match */ |
| rtc = system_get_rtc_sec_subsec(&rtcss) + seconds; |
| rtcss += HIB_RTC_USEC_TO_SUBSEC(microseconds); |
| if (rtcss > 0x7fff) { |
| rtc += rtcss >> 15; |
| rtcss &= 0x7fff; |
| } |
| |
| /* Set RTC alarm match */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBRTCM0 = rtc; |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBRTCSS = rtcss << 16; |
| wait_for_hibctl_wc(); |
| } |
| |
| /** |
| * Use hibernate module to set up an RTC interrupt at a given |
| * time from now |
| * |
| * @param seconds Number of seconds before RTC interrupt |
| * @param microseconds Number of microseconds before RTC interrupt |
| */ |
| void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds) |
| { |
| /* Clear pending interrupt */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS; |
| |
| /* Set match time */ |
| set_hibernate_rtc_match_time(seconds, microseconds); |
| |
| /* Enable RTC interrupt on match */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIM = 1; |
| |
| /* |
| * Wait for the write to commit. This ensures that the RTC interrupt |
| * actually gets enabled. This is important if we're about to switch |
| * the system to the 30 kHz oscillator, which might prevent the write |
| * from committing. |
| */ |
| wait_for_hibctl_wc(); |
| } |
| |
| /** |
| * Disable and clear the RTC interrupt. |
| */ |
| void system_reset_rtc_alarm(void) |
| { |
| /* Disable hibernate interrupts */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIM = 0; |
| |
| /* Clear interrupts */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS; |
| } |
| |
| /** |
| * Hibernate module interrupt |
| */ |
| void __hibernate_irq(void) |
| { |
| system_reset_rtc_alarm(); |
| } |
| DECLARE_IRQ(LM4_IRQ_HIBERNATE, __hibernate_irq, 1); |
| |
| /** |
| * Enable hibernate interrupt |
| */ |
| void system_enable_hib_interrupt(void) |
| { |
| task_enable_irq(LM4_IRQ_HIBERNATE); |
| } |
| |
| /** |
| * Internal hibernate function. |
| * |
| * @param seconds Number of seconds to sleep before RTC alarm |
| * @param microseconds Number of microseconds to sleep before RTC alarm |
| * @param flags Additional hibernate wake flags |
| */ |
| static void hibernate(uint32_t seconds, uint32_t microseconds, uint32_t flags) |
| { |
| uint32_t hibctl; |
| |
| /* Set up wake reasons and hibernate flags */ |
| hibctl = LM4_HIBERNATE_HIBCTL | LM4_HIBCTL_PINWEN; |
| |
| if (flags & HIBDATA_WAKE_PIN) |
| hibctl |= LM4_HIBCTL_PINWEN; |
| else |
| hibctl &= ~LM4_HIBCTL_PINWEN; |
| |
| if (seconds || microseconds) { |
| hibctl |= LM4_HIBCTL_RTCWEN; |
| flags |= HIBDATA_WAKE_RTC; |
| |
| set_hibernate_rtc_match_time(seconds, microseconds); |
| |
| /* Enable RTC interrupt on match */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIM = 1; |
| } else { |
| hibctl &= ~LM4_HIBCTL_RTCWEN; |
| } |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBCTL = hibctl; |
| |
| /* Clear pending interrupt */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS; |
| |
| /* Store hibernate flags */ |
| hibdata_write(HIBDATA_INDEX_WAKE, flags); |
| |
| __enter_hibernate(hibctl | LM4_HIBCTL_HIBREQ); |
| } |
| |
| void system_hibernate(uint32_t seconds, uint32_t microseconds) |
| { |
| /* Flush console before hibernating */ |
| cflush(); |
| hibernate(seconds, microseconds, HIBDATA_WAKE_PIN); |
| } |
| |
| void chip_pre_init(void) |
| { |
| /* Enable clocks to GPIO block C in run and sleep modes. */ |
| clock_enable_peripheral(CGC_OFFSET_GPIO, 0x0004, CGC_MODE_ALL); |
| |
| /* |
| * Ensure PC0:3 are set to JTAG function. They should be set this way |
| * on a cold boot, but on a warm reboot a previous misbehaving image |
| * could have set them differently. |
| */ |
| if (((LM4_GPIO_PCTL(LM4_GPIO_C) & 0x0000ffff) == 0x00001111) && |
| ((LM4_GPIO_AFSEL(LM4_GPIO_C) & 0x0f) == 0x0f) && |
| ((LM4_GPIO_DEN(LM4_GPIO_C) & 0x0f) == 0x0f) && |
| ((LM4_GPIO_PUR(LM4_GPIO_C) & 0x0f) == 0x0f)) |
| return; /* Already properly configured */ |
| |
| /* Unlock commit register for JTAG pins */ |
| LM4_GPIO_LOCK(LM4_GPIO_C) = LM4_GPIO_LOCK_UNLOCK; |
| LM4_GPIO_CR(LM4_GPIO_C) |= 0x0f; |
| |
| /* Reset JTAG pins */ |
| LM4_GPIO_PCTL(LM4_GPIO_C) = |
| (LM4_GPIO_PCTL(LM4_GPIO_C) & 0xffff0000) | 0x00001111; |
| LM4_GPIO_AFSEL(LM4_GPIO_C) |= 0x0f; |
| LM4_GPIO_DEN(LM4_GPIO_C) |= 0x0f; |
| LM4_GPIO_PUR(LM4_GPIO_C) |= 0x0f; |
| |
| /* Set interrupt on either edge of the JTAG signals */ |
| LM4_GPIO_IS(LM4_GPIO_C) &= ~0x0f; |
| LM4_GPIO_IBE(LM4_GPIO_C) |= 0x0f; |
| |
| /* Re-lock commit register */ |
| LM4_GPIO_CR(LM4_GPIO_C) &= ~0x0f; |
| LM4_GPIO_LOCK(LM4_GPIO_C) = 0; |
| } |
| |
| void system_pre_init(void) |
| { |
| uint32_t hibctl; |
| #ifdef CONFIG_SOFTWARE_PANIC |
| uint32_t reason, info; |
| uint8_t exception; |
| #endif |
| |
| /* |
| * Enable clocks to the hibernation module in run, sleep, |
| * and deep sleep modes. |
| */ |
| clock_enable_peripheral(CGC_OFFSET_HIB, 0x1, CGC_MODE_ALL); |
| |
| /* |
| * Enable the hibernation oscillator, if it's not already enabled. |
| * This should only need setting if the EC completely lost power (for |
| * example, the battery was pulled). |
| */ |
| if (!(LM4_HIBERNATE_HIBCTL & LM4_HIBCTL_CLK32EN)) { |
| int i; |
| |
| /* Enable clock to hibernate module */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBCTL |= LM4_HIBCTL_CLK32EN; |
| |
| /* Wait for write-complete */ |
| for (i = 0; i < 1000000; i++) { |
| if (LM4_HIBERNATE_HIBRIS & 0x10) |
| break; |
| } |
| |
| /* Enable and reset RTC */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBCTL |= LM4_HIBCTL_RTCEN; |
| system_set_rtc(0); |
| |
| /* Clear all hibernate data entries */ |
| for (i = 0; i < LM4_HIBERNATE_HIBDATA_ENTRIES; i++) |
| hibdata_write(i, 0); |
| } |
| |
| /* |
| * Set wake reasons to RTC match and WAKE pin by default. |
| * Before going in to hibernate, these may change. |
| */ |
| hibctl = LM4_HIBERNATE_HIBCTL; |
| hibctl |= LM4_HIBCTL_RTCWEN; |
| hibctl |= LM4_HIBCTL_PINWEN; |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBCTL = hibctl; |
| |
| /* |
| * Initialize registers after reset to work around LM4 chip errata |
| * (still present in A3 chip stepping). |
| */ |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBRTCT = 0x7fff; |
| wait_for_hibctl_wc(); |
| LM4_HIBERNATE_HIBIM = 0; |
| |
| check_reset_cause(); |
| |
| #ifdef CONFIG_SOFTWARE_PANIC |
| /* Restore then clear saved panic reason */ |
| reason = hibdata_read(HIBDATA_INDEX_SAVED_PANIC_REASON); |
| info = hibdata_read(HIBDATA_INDEX_SAVED_PANIC_INFO); |
| exception = hibdata_read(HIBDATA_INDEX_SAVED_PANIC_EXCEPTION); |
| if (reason || info || exception) { |
| panic_set_reason(reason, info, exception); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_REASON, 0); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_INFO, 0); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_EXCEPTION, 0); |
| } |
| #endif |
| |
| /* Initialize bootcfg if needed */ |
| if (LM4_SYSTEM_BOOTCFG != CONFIG_BOOTCFG_VALUE) { |
| /* read-modify-write */ |
| LM4_FLASH_FMD = (LM4_SYSTEM_BOOTCFG_MASK & LM4_SYSTEM_BOOTCFG) |
| | (~LM4_SYSTEM_BOOTCFG_MASK & CONFIG_BOOTCFG_VALUE); |
| LM4_FLASH_FMA = 0x75100000; |
| LM4_FLASH_FMC = 0xa4420008; /* WRKEY | COMT */ |
| while (LM4_FLASH_FMC & 0x08) |
| ; |
| } |
| |
| /* Brown-outs should trigger a reset */ |
| LM4_SYSTEM_PBORCTL |= 0x02; |
| } |
| |
| void system_reset(int flags) |
| { |
| uint32_t save_flags = 0; |
| |
| /* Disable interrupts to avoid task swaps during reboot */ |
| interrupt_disable(); |
| |
| /* Save current reset reasons if necessary */ |
| if (flags & SYSTEM_RESET_PRESERVE_FLAGS) |
| save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED; |
| |
| if (flags & SYSTEM_RESET_LEAVE_AP_OFF) |
| save_flags |= RESET_FLAG_AP_OFF; |
| |
| hibdata_write(HIBDATA_INDEX_SAVED_RESET_FLAGS, save_flags); |
| |
| if (flags & SYSTEM_RESET_HARD) { |
| #ifdef CONFIG_SOFTWARE_PANIC |
| uint32_t reason, info; |
| uint8_t exception; |
| |
| /* Panic data will be wiped by hard reset, so save it */ |
| panic_get_reason(&reason, &info, &exception); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_REASON, reason); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_INFO, info); |
| hibdata_write(HIBDATA_INDEX_SAVED_PANIC_EXCEPTION, exception); |
| #endif |
| |
| /* |
| * Bounce through hibernate to trigger a hard reboot. Do |
| * not wake on wake pin, since we need the full duration. |
| */ |
| hibernate(0, HIB_RESET_USEC, HIBDATA_WAKE_HARD_RESET); |
| } else |
| CPU_NVIC_APINT = 0x05fa0004; |
| |
| /* Spin and wait for reboot; should never return */ |
| while (1) |
| ; |
| } |
| |
| int system_set_scratchpad(uint32_t value) |
| { |
| return hibdata_write(HIBDATA_INDEX_SCRATCHPAD, value); |
| } |
| |
| uint32_t system_get_scratchpad(void) |
| { |
| return hibdata_read(HIBDATA_INDEX_SCRATCHPAD); |
| } |
| |
| const char *system_get_chip_vendor(void) |
| { |
| return "ti"; |
| } |
| |
| static char to_hex(int x) |
| { |
| if (x >= 0 && x <= 9) |
| return '0' + x; |
| return 'a' + x - 10; |
| } |
| |
| const char *system_get_chip_id_string(void) |
| { |
| static char str[15] = "Unknown-"; |
| char *p = str + 8; |
| uint32_t did = LM4_SYSTEM_DID1 >> 16; |
| |
| if (*p) |
| return (const char *)str; |
| |
| *p = to_hex(did >> 12); |
| *(p + 1) = to_hex((did >> 8) & 0xf); |
| *(p + 2) = to_hex((did >> 4) & 0xf); |
| *(p + 3) = to_hex(did & 0xf); |
| *(p + 4) = '\0'; |
| |
| return (const char *)str; |
| } |
| |
| const char *system_get_raw_chip_name(void) |
| { |
| switch ((LM4_SYSTEM_DID1 & 0xffff0000) >> 16) { |
| case 0x10de: |
| return "tm4e1g31h6zrb"; |
| case 0x10e2: |
| return "lm4fsxhh5bb"; |
| case 0x10e3: |
| return "lm4fs232h5bb"; |
| case 0x10e4: |
| return "lm4fs99h5bb"; |
| case 0x10e6: |
| return "lm4fs1ah5bb"; |
| case 0x10ea: |
| return "lm4fs1gh5bb"; |
| default: |
| return system_get_chip_id_string(); |
| } |
| } |
| |
| const char *system_get_chip_name(void) |
| { |
| const char *postfix = "-tm"; /* test mode */ |
| static char str[20]; |
| const char *raw_chip_name = system_get_raw_chip_name(); |
| char *p = str; |
| |
| if (LM4_TEST_MODE_ENABLED) { |
| /* Debug mode is enabled. Postfix chip name. */ |
| while (*raw_chip_name) |
| *(p++) = *(raw_chip_name++); |
| while (*postfix) |
| *(p++) = *(postfix++); |
| *p = '\0'; |
| return (const char *)str; |
| } else { |
| return raw_chip_name; |
| } |
| } |
| |
| int system_get_bbram(enum system_bbram_idx idx, uint8_t *value) |
| { |
| return EC_ERROR_UNIMPLEMENTED; |
| } |
| |
| int system_set_bbram(enum system_bbram_idx idx, uint8_t value) |
| { |
| return EC_ERROR_UNIMPLEMENTED; |
| } |
| |
| const char *system_get_chip_revision(void) |
| { |
| static char rev[3]; |
| |
| /* Extract the major[15:8] and minor[7:0] revisions. */ |
| rev[0] = 'A' + ((LM4_SYSTEM_DID0 >> 8) & 0xff); |
| rev[1] = '0' + (LM4_SYSTEM_DID0 & 0xff); |
| rev[2] = 0; |
| |
| return rev; |
| } |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| #ifdef CONFIG_CMD_RTC |
| void print_system_rtc(enum console_channel ch) |
| { |
| uint32_t rtc; |
| uint32_t rtcss; |
| |
| rtc = system_get_rtc_sec_subsec(&rtcss); |
| cprintf(ch, "RTC: 0x%08x.%04x (%d.%06d s)\n", |
| rtc, rtcss, rtc, HIB_RTC_SUBSEC_TO_USEC(rtcss)); |
| } |
| |
| static int command_system_rtc(int argc, char **argv) |
| { |
| if (argc == 3 && !strcasecmp(argv[1], "set")) { |
| char *e; |
| uint32_t t = strtoi(argv[2], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM2; |
| |
| system_set_rtc(t); |
| } else if (argc > 1) { |
| return EC_ERROR_INVAL; |
| } |
| |
| print_system_rtc(CC_COMMAND); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc, |
| "[set <seconds>]", |
| "Get/set real-time clock"); |
| |
| #ifdef CONFIG_CMD_RTC_ALARM |
| /** |
| * Test the RTC alarm by setting an interrupt on RTC match. |
| */ |
| static int command_rtc_alarm_test(int argc, char **argv) |
| { |
| int s = 1, us = 0; |
| char *e; |
| |
| ccprintf("Setting RTC alarm\n"); |
| system_enable_hib_interrupt(); |
| |
| if (argc > 1) { |
| s = strtoi(argv[1], &e, 10); |
| if (*e) |
| return EC_ERROR_PARAM1; |
| |
| } |
| if (argc > 2) { |
| us = strtoi(argv[2], &e, 10); |
| if (*e) |
| return EC_ERROR_PARAM2; |
| |
| } |
| |
| system_set_rtc_alarm(s, us); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test, |
| "[seconds [microseconds]]", |
| "Test alarm"); |
| #endif /* CONFIG_CMD_RTC_ALARM */ |
| #endif /* CONFIG_CMD_RTC */ |
| |
| /*****************************************************************************/ |
| /* Host commands */ |
| |
| #ifdef CONFIG_HOSTCMD_RTC |
| static int system_rtc_get_value(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_rtc *r = args->response; |
| |
| r->time = system_get_rtc_sec_subsec(NULL); |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_RTC_GET_VALUE, |
| system_rtc_get_value, |
| EC_VER_MASK(0)); |
| |
| static int system_rtc_set_value(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_rtc *p = args->params; |
| |
| system_set_rtc(p->time); |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_VALUE, |
| system_rtc_set_value, |
| EC_VER_MASK(0)); |
| #endif /* CONFIG_HOSTCMD_RTC */ |