/* * Copyright (C) 2014 Apple Inc. All rights reserved. * * This document is the property of Apple Inc. * It is considered confidential and proprietary. * * This document may not be reproduced or transmitted in any form, * in whole or in part, without the express written permission of * Apple Inc. */ #include #include #include #include #include #include #include #include #include #include enum { kRTKitArgBlockVersion1 = 'RTK1', kDaliFWArgVersion1 = 'DAL1', kDaliFWArgVersion2 = 'DAL2', kDaliFWArgVersion3 = 'DAL3', kDaliFWArgVersion4 = 'DAL4' }; enum { kMode24hEnabledOffset = 0, kRotateScreenOffset = 1, kDaliDarkWakeOffset = 2, /* 0 = normal wake, 1 = scheduled thermal wake, don't paint screen */ kPmuDebugOffset = 3, kIsChargerConnectedOffset = 4, kPrechargeNeededOffset = 5 }; #define kUTCOffsetUnsetValue 0x7fffffffffffffffLL /* INT64_MAX. There's only 86400 seconds in a day */ typedef struct dali_payload_t { uint32_t magic; /* = kDaliFWArgVersion3 */ uint32_t reserved; uint8_t charFlags[8]; int64_t utcOffset; /* Set to either a valid offset, or kUTCOffsetUnsetValue if unset */ DMin_t dmin; } dali_payload_t; typedef struct dali_args_t { uint32_t magic; /* = kRTKitArgBlockVersion1 */ uint32_t size; /* size of pending payload */ dali_payload_t payload; } dali_args_t; #define kDMinNVRAMKey "device-material" static void save_dmin_to_nvram(const DMin_t* dmin) { #if WITH_LLB_NVRAM if (target_needs_chargetrap()) panic("LLB NVRAM is read-only, it's a logic error to cache NVRAM here"); #endif char dmin_ascii[2*sizeof(DMin_t) + 1]; const char* dmin_buffer = (const char*)(dmin); for(unsigned long i = 0; i < sizeof(DMin_t); i++) { snprintf(dmin_ascii + 2*i, sizeof(dmin_ascii) - 2*i, "%02X", dmin_buffer[i]); } dmin_ascii[2*sizeof(DMin_t)] = '\0'; env_set(kDMinNVRAMKey, dmin_ascii, ENV_PERSISTENT); // dprintf(DEBUG_INFO, "save_dmin_to_nvram: Set kDMinNVRAMKey = %s\n", dmin_ascii); } static int get_dmin_from_nvram(DMin_t* dest_dmin) { char* dest_buf = (char*)(dest_dmin); const char* dmin_val = env_get(kDMinNVRAMKey); if (dmin_val == NULL) { // dprintf(DEBUG_INFO, "No DMin cached in NVRAM yet\n"); return -1; } unsigned int dmin_len = (unsigned int)strlen(dmin_val); if (dmin_len > 2*sizeof(DMin_t) || (dmin_len & 0x1)) { dprintf(DEBUG_CRITICAL, "Unexpected DMin key length %u, expected even and <= %zu\n", dmin_len, 2*sizeof(DMin_t)); return -2; } unsigned long i = 0; unsigned long p = 0; while ( i < dmin_len ) { unsigned char byte = 0; for (int j = 0; j < 2; j++) { int shift_factor = 4*(1-j); switch(dmin_val[i]) { case '0' ... '9': byte += (dmin_val[i] - '0') << shift_factor; break; case 'A' ... 'F': byte += ((dmin_val[i] - 'A') + 10) << shift_factor; break; default: dprintf(DEBUG_CRITICAL, "Non-hex digit in DMin NVRAM key: 0x%x\n", dmin_val[i]); return -3; } i++; } dest_buf[p++] = byte; } return 0; } void target_init_fast_dali() { DMin_t dmin; bzero(&dmin, sizeof(DMin_t)); syscfgCopyDataForTag('DMin', (uint8_t*)&(dmin), sizeof(DMin_t)); save_dmin_to_nvram(&dmin); } static bool is_dark_wake() { bool powersupply_change_event, button_event, other_wake_event; pmu_check_events(&powersupply_change_event, &button_event, &other_wake_event); dprintf(DEBUG_INFO, "is_dark_wake: ps_changed = %d, button = %d, other = %d\n", powersupply_change_event, button_event, other_wake_event); return !powersupply_change_event && !button_event; } void* target_prepare_dali(void) { #if defined(PMU_LDO_OPAL) && WITH_HW_POWER // Interim Solution: Turn off LDO8 in Dali mode/iBoot // Opal uses a lot of power when held in reset. For sure we don't need it in Dali. power_enable_ldo(PMU_LDO_OPAL, false); #endif #ifdef GPIO_SPU_TO_OPAL_CS_L gpio_configure(GPIO_SPU_TO_OPAL_CS_L, GPIO_CFG_IN); #endif dali_args_t* args = (dali_args_t *)PANIC_BASE; bzero(args, sizeof(dali_args_t)); args->magic = kRTKitArgBlockVersion1; args->size = sizeof(dali_payload_t); args->payload.magic = kDaliFWArgVersion3; args->payload.charFlags[kMode24hEnabledOffset] = (env_get_uint("dali-24h-mode", 0) == 1); args->payload.charFlags[kRotateScreenOffset] = (env_get_uint("display-rotation", 0) == 1); args->payload.charFlags[kPmuDebugOffset] = (env_get_uint("dali-pmu-debug", 0) == 1); #if WITH_HW_POWER args->payload.charFlags[kDaliDarkWakeOffset] = (is_dark_wake()); args->payload.charFlags[kIsChargerConnectedOffset] = (power_has_usb()); args->payload.charFlags[kPrechargeNeededOffset] = (power_needs_precharge()); dprintf(DEBUG_INFO, "Charger Connected: %d, Precharge needed: %d\n", (int)args->payload.charFlags[kIsChargerConnectedOffset], (int)args->payload.charFlags[kPrechargeNeededOffset]); #endif if (get_dmin_from_nvram(&(args->payload.dmin)) < 0) { dprintf(DEBUG_CRITICAL, "Unable to find DMin in NVRAM; booting into Dali mode with potentially incorrect DMin information!\n"); } args->payload.utcOffset = kUTCOffsetUnsetValue; const char* utcOffsetStr = env_get("utc-offset"); if (utcOffsetStr) { args->payload.utcOffset = atoi(utcOffsetStr); } return args; }