/* * Copyright (c) 2017, Theobroma Systems Design und Consulting GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its contributors may be used * to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "sunxi_private.h" #define SUNXI_ARISC_EMULATOR_SVC_BASE 0xc0000000 /* fast + SMC64 */ /* Standby commands */ #define ARM_SVC_ARISC_STANDBY_INFO_REQ \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x20) #define ARM_SVC_ARISC_QUERY_WAKEUP_SRC_REQ \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x23) /* DVFS commands */ #define ARM_SVC_ARISC_CPUX_DVFS_REQ \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x30) /* PMU commands */ #define ARM_SVC_ARISC_AXP_DISABLE_IRQ \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x51) #define ARM_SVC_ARISC_AXP_ENABLE_IRQ \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x52) #define ARM_SVC_ARISC_AXP_GET_CHIP_ID \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x53) #define ARM_SVC_ARISC_AXP_SET_PARAS \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x54) #define ARM_SVC_ARISC_SET_PMU_VOLT \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x55) #define ARM_SVC_ARISC_GET_PMU_VOLT \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x56) #define ARM_SVC_ARISC_SET_LED_BLN \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x57) #define ARM_SVC_ARISC_SET_PWR_TREE \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x59) #define ARM_SVC_ARISC_CLR_NMI_STATUS \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x5a) #define ARM_SVC_ARISC_SET_NMI_TRIGGER \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x5b) /* Debug commands */ #define ARM_SVC_ARISC_SET_DEBUG_LEVEL \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x60) #define ARM_SVC_ARISC_MESSAGE_LOOPBACK \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x61) #define ARM_SVC_ARISC_SET_UART_BAUDRATE \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x62) #define ARM_SVC_ARISC_SET_DEBUG_DRAM_CRC_PARAS \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x64) /* RSB commands */ #define ARM_SVC_ARISC_RSB_READ_BLOCK_DATA \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x80) #define ARM_SVC_ARISC_RSB_WRITE_BLOCK_DATA \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x81) #define ARM_SVC_ARISC_RSB_BITS_OPS_SYNC \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x82) #define ARM_SVC_ARISC_RSB_SET_INTERFACE_MODE \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x83) #define ARM_SVC_ARISC_RSB_SET_RTSADDR \ (SUNXI_ARISC_EMULATOR_SVC_BASE + 0x84) static int32_t sunxi_arisc_emulator_setup(void) { return 0; } /* Syntactic sugar for CPU regulator */ static inline int sunxi_pmic_set_cpu_mvolt(uint32_t mvolt) { return sunxi_pmic_set_voltage(AXP803_DCDC2, mvolt); } /* Empirically derived operating points */ static uint32_t mvolt_for_mhz(uint32_t mhz) { if (mhz >= 1200) return 1300; else if (mhz >= 1008) return 1200; else if (mhz >= 816) return 1100; else return 1040; } static uint64_t sunxi_arisc_emulator_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, void *cookie, void *handle, uint64_t flags) { switch (smc_fid) { case ARM_SVC_ARISC_STANDBY_INFO_REQ: /* read/write power states information * x1...&temp_paras (9 32-bit values) * x2...op (0..READ, 1..WRITE) * * Dump from an Arisc-based ATF response. See files: * - /sys/bus/platform/devices/arisc.3/sst_power_real_info * - /sys/bus/platform/devices/arisc.3/dram_crc_result * * temp_paras: * power_state: * u32 enable -> 0 * u32 power_reg -> 0 * s32 system_power -> 0 * dram_state: * u32 dram_crc_enable -> 0 * u32 dram_crc_src -> 0x40000000 * u32 dram_crc_len -> 0x100000 * u32 dram_crc_error -> 0 * u32 dram_crc_total_count -> 0 * u32 dram_crc_error_count -> 0 * */ if (x2 == 0) memset((void *)x1, 0, 36); SMC_RET1(handle, 0); case ARM_SVC_ARISC_QUERY_WAKEUP_SRC_REQ: /* Return the resume-from-standby source * x1...phys(&event_32) * Set hard-coded to 0x1 (wakeup key) */ *(uint32_t *)x1 = 0x1; SMC_RET1(handle, 0); case ARM_SVC_ARISC_CPUX_DVFS_REQ: { /* Set CPU frequency * x1...freq [kHz] * x2...pll (1 for PLL1) * x3...mode (alwasys 1) * returns non-zero on error * * Exported via /sys/bus/platform/devices/arisc.3/freq */ if (x2 != 1 || x3 != 1) SMC_RET1(handle, 1); uint32_t old_freq_mhz = sun50i_get_cpu_pll(); uint32_t new_freq_mhz = x1 / 1000; if (old_freq_mhz < new_freq_mhz) { sunxi_pmic_set_cpu_mvolt(mvolt_for_mhz(x1/1000)); sun50i_set_cpu_pll(x1 / 1000); } else { sun50i_set_cpu_pll(x1 / 1000); sunxi_pmic_set_cpu_mvolt(mvolt_for_mhz(x1/1000)); } SMC_RET1(handle, 0); } case ARM_SVC_ARISC_AXP_DISABLE_IRQ: /* fall through */ case ARM_SVC_ARISC_AXP_ENABLE_IRQ: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ case ARM_SVC_ARISC_AXP_GET_CHIP_ID: { uint8_t val; sunxi_pmic_read(0x03, &val); SMC_RET1(handle, val); } case ARM_SVC_ARISC_AXP_SET_PARAS: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ case ARM_SVC_ARISC_SET_PMU_VOLT: /* fall through */ case ARM_SVC_ARISC_GET_PMU_VOLT: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ case ARM_SVC_ARISC_SET_LED_BLN: case ARM_SVC_ARISC_SET_PWR_TREE: case ARM_SVC_ARISC_CLR_NMI_STATUS: case ARM_SVC_ARISC_SET_NMI_TRIGGER: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ case ARM_SVC_ARISC_SET_DEBUG_LEVEL: /* fall through */ case ARM_SVC_ARISC_MESSAGE_LOOPBACK: /* fall through */ case ARM_SVC_ARISC_SET_UART_BAUDRATE: /* fall through */ case ARM_SVC_ARISC_SET_DEBUG_DRAM_CRC_PARAS: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ case ARM_SVC_ARISC_RSB_READ_BLOCK_DATA: { /* Read RSB * x1...phys(paras) * returns non-zero on error * * paras: array of 32 bit values: * paras[0]: (len16|datatype16) * paras[1]: devaddr * paras[2]: regaddr3..0 * paras[3]: data0 * paras[4]: data1 * paras[5]: data2 * paras[6]: data3 */ int ret; size_t i; uint32_t *paras = (uint32_t *)x1; uint8_t rt_addr = (uint8_t)paras[1]; uint16_t len = paras[0] & 0xffff; uint16_t datatype = (paras[0] >> 16) & 0xffff; if (len > 4) { ERROR("RSB Read with len %u not supported!\n", len); SMC_RET1(handle, 1); } for (i = 0; i < len; i++) { uint8_t addr = paras[2] >> (i*8); uint32_t *data = ¶s[3+i]; ret = rsb_read(rt_addr, addr, data, datatype); if (ret) { WARN("RSB read failed!\n"); SMC_RET1(handle, 1); } } SMC_RET1(handle, 0); } case ARM_SVC_ARISC_RSB_WRITE_BLOCK_DATA: { /* Write RSB * x1...phys(paras) * returns non-zero on error * * paras: array of 32 bit values: * paras[0]: (len16|datatype16) * paras[1]: devaddr * paras[2]: regaddr3..0 * paras[3]: data0 * paras[4]: data1 * paras[5]: data2 * paras[6]: data3 */ int ret; size_t i; uint32_t *paras = (uint32_t *)x1; uint8_t rt_addr = (uint8_t)paras[1]; uint16_t len = paras[0] & 0xffff; uint16_t datatype = (paras[0] >> 16) & 0xffff; if (len > 4) { ERROR("RSB Read with len %u not supported!\n", len); SMC_RET1(handle, 1); } for (i = 0; i < len; i++) { uint8_t addr = paras[2] >> (i*8); uint32_t data = paras[3+i]; ret = rsb_write(rt_addr, addr, data, datatype); if (ret) { WARN("RSB read failed!\n"); SMC_RET1(handle, 1); } } SMC_RET1(handle, 0); } case ARM_SVC_ARISC_RSB_BITS_OPS_SYNC: case ARM_SVC_ARISC_RSB_SET_INTERFACE_MODE: case ARM_SVC_ARISC_RSB_SET_RTSADDR: WARN("%s: function call (0x%x) ignored!\n", __func__, smc_fid); SMC_RET1(handle, 0); /* We do nothing */ default: WARN("sunxi_arisc_emulator: Not supported function: 0x%x.\n", smc_fid); SMC_RET1(handle, SMC_UNK); } } /* Register service call as runtime service */ DECLARE_RT_SVC( sunxi_arisc_emulator, OEN_ARM_START, OEN_ARM_END, SMC_TYPE_FAST, sunxi_arisc_emulator_setup, sunxi_arisc_emulator_handler );