/* * Copyright (c) 2022, Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "ddr.h" #include #include "socfpga_handoff.h" int ddr_calibration_check(void) { // DDR calibration check int status = 0; uint32_t u32data_read = 0; NOTICE("DDR: Access address 0x%x:...\n", IO96B_0_REG_BASE); u32data_read = mmio_read_32(IO96B_0_REG_BASE); NOTICE("DDR: Access address 0x%x: read 0x%04x\n", IO96B_0_REG_BASE, u32data_read); if (u32data_read == -EPERM) { status = -EPERM; assert(u32data_read); } u32data_read = 0x0; NOTICE("DDR: Access address 0x%x: ...\n", IO96B_1_REG_BASE); u32data_read = mmio_read_32(IO96B_1_REG_BASE); NOTICE("DDR: Access address 0x%x: read 0x%04x\n", IO96B_1_REG_BASE, u32data_read); if (u32data_read == -EPERM) { status = -EPERM; assert(u32data_read); } return status; } int iossm_mb_init(void) { // int status; // Update according to IOSSM mailbox spec // if (status) { // return status; // } return 0; } int wait_respond(uint16_t timeout) { uint32_t status = 0; uint32_t count = 0; uint32_t data = 0; /* Wait status command response ready */ do { data = mmio_read_32(IO96B_CSR_REG(CMD_RESPONSE_STATUS)); count++; if (count >= timeout) { return -ETIMEDOUT; } } while (STATUS_COMMAND_RESPONSE(data) != STATUS_COMMAND_RESPONSE_READY); status = (data & STATUS_GENERAL_ERROR_MASK) >> STATUS_GENERAL_ERROR_OFFSET; if (status != 0) { return status; } status = (data & STATUS_CMD_RESPONSE_ERROR_MASK) >> STATUS_CMD_RESPONSE_ERROR_OFFSET; if (status != 0) { return status; } return status; } int iossm_mb_read_response(void) { uint32_t status = 0; unsigned int i; uint32_t resp_data[IOSSM_RESP_MAX_WORD_SIZE]; uint32_t resp_param_reg; // Check STATUS_CMD_RESPONSE_DATA_PTR_VALID in // STATUS_COMMAND_RESPONSE to ensure data pointer response /* Read CMD_RESPONSE_STATUS and CMD_RESPONSE_DATA_* */ resp_data[0] = mmio_read_32(IO96B_CSR_REG(CMD_RESPONSE_STATUS)); resp_data[0] = (resp_data[0] & CMD_RESPONSE_DATA_SHORT_MASK) >> CMD_RESPONSE_DATA_SHORT_OFFSET; resp_param_reg = CMD_RESPONSE_STATUS; for (i = 1; i < IOSSM_RESP_MAX_WORD_SIZE; i++) { resp_param_reg = resp_param_reg - CMD_RESPONSE_OFFSET; resp_data[i] = mmio_read_32(IO96B_CSR_REG(resp_param_reg)); } /* Wait for STATUS_COMMAND_RESPONSE_READY*/ status = wait_respond(1000); /* Read CMD_RESPONSE_STATUS and CMD_RESPONSE_DATA_* */ mmio_setbits_32(STATUS_COMMAND_RESPONSE(IO96B_CSR_REG( CMD_RESPONSE_STATUS)), STATUS_COMMAND_RESPONSE_READY_CLEAR); return status; } int iossm_mb_send(uint32_t cmd_target_ip_type, uint32_t cmd_target_ip_instance_id, uint32_t cmd_type, uint32_t cmd_opcode, uint32_t *args, unsigned int len) { unsigned int i; uint32_t status = 0; uint32_t cmd_req; uint32_t cmd_param_reg; cmd_target_ip_type = (cmd_target_ip_type & CMD_TARGET_IP_TYPE_MASK) << CMD_TARGET_IP_TYPE_OFFSET; cmd_target_ip_instance_id = (cmd_target_ip_instance_id & CMD_TARGET_IP_INSTANCE_ID_MASK) << CMD_TARGET_IP_INSTANCE_ID_OFFSET; cmd_type = (cmd_type & CMD_TYPE_MASK) << CMD_TYPE_OFFSET; cmd_opcode = (cmd_opcode & CMD_OPCODE_MASK) << CMD_OPCODE_OFFSET; cmd_req = cmd_target_ip_type | cmd_target_ip_instance_id | cmd_type | cmd_opcode; /* send mailbox request */ IOSSM_MB_WRITE(IO96B_CSR_REG(CMD_REQ), cmd_req); if (len != 0) { cmd_param_reg = CMD_REQ; for (i = 0; i < len; i++) { cmd_param_reg = cmd_param_reg - CMD_PARAM_OFFSET; IOSSM_MB_WRITE(IO96B_CSR_REG(cmd_param_reg), args[i]); } } status = iossm_mb_read_response(); if (status != 0) { return status; } return status; } int ddr_iossm_mailbox_cmd(uint32_t cmd_opcode) { // IOSSM uint32_t status = 0; unsigned int i = 0; uint32_t payload[IOSSM_CMD_MAX_WORD_SIZE] = {0U}; switch (cmd_opcode) { case CMD_INIT: status = iossm_mb_init(); break; case OPCODE_GET_MEM_INTF_INFO: status = iossm_mb_send(0, 0, MBOX_CMD_GET_SYS_INFO, OPCODE_GET_MEM_INTF_INFO, payload, i); break; case OPCODE_GET_MEM_TECHNOLOGY: status = iossm_mb_send(0, 0, MBOX_CMD_GET_MEM_INFO, OPCODE_GET_MEM_TECHNOLOGY, payload, i); break; case OPCODE_GET_MEM_WIDTH_INFO: status = iossm_mb_send(0, 0, MBOX_CMD_GET_MEM_INFO, OPCODE_GET_MEM_WIDTH_INFO, payload, i); break; case OPCODE_ECC_ENABLE_STATUS: status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_ECC_ENABLE_STATUS, payload, i); break; case OPCODE_ECC_INTERRUPT_MASK: // payload[i] = CMD_PARAM_0 [16:0]: ECC_INTERRUPT_MASK status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_ECC_INTERRUPT_MASK, payload, i); break; case OPCODE_ECC_SCRUB_MODE_0_START: // payload[i] = CMD_PARAM_0 [15:0]: ECC_SCRUB_INTERVAL //i++; // payload[i] = CMD_PARAM_1 [11:0]: ECC_SCRUB_LEN //i++; // payload[i] = CMD_PARAM_2 [0:0]: ECC_SCRUB_FULL_MEM //i++; // payload[i]= CMD_PARAM_3 [31:0]: ECC_SCRUB_START_ADDR [31:0] //i++; // payload[i] = CMD_PARAM_4 [5:0]: ECC_SCRUB_START_ADDR [36:32] //i++; // payload[i] = CMD_PARAM_5 [31:0]: ECC_SCRUB_END_ADDR [31:0] //i++; // payload[i] = CMD_PARAM_6 [5:0]: ECC_SCRUB_END_ADDR [36:32] //i++; status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_ECC_SCRUB_MODE_0_START, payload, i); break; case OPCODE_ECC_SCRUB_MODE_1_START: // payload[i] = CMD_PARAM_0 [15:0]: ECC_SCRUB_IDLE_CNT //i++; // payload[i] = CMD_PARAM_1 [11:0]: ECC_SCRUB_LEN //i++; // payload[i] = CMD_PARAM_2 [0:0]: ECC_SCRUB_FULL_MEM //i++; // payload[i] = CMD_PARAM_3 [31:0]: ECC_SCRUB_START_ADDR [31:0] //i++; // payload[i] = CMD_PARAM_4 [5:0]: ECC_SCRUB_START_ADDR [36:32] //i++; // payload[i] = CMD_PARAM_5 [31:0]: ECC_SCRUB_END_ADDR [31:0] //i++; // payload[i] = CMD_PARAM_6 [5:0]: ECC_SCRUB_END_ADDR [36:32] //i++; status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_ECC_SCRUB_MODE_1_START, payload, i); break; case OPCODE_BIST_RESULTS_STATUS: status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_BIST_RESULTS_STATUS, payload, i); break; case OPCODE_BIST_MEM_INIT_START: status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_CONTROLLER_OP, OPCODE_BIST_MEM_INIT_START, payload, i); break; case OPCODE_TRIG_MEM_CAL: status = iossm_mb_send(0, 0, MBOX_CMD_TRIG_MEM_CAL_OP, OPCODE_TRIG_MEM_CAL, payload, i); break; default: break; } if (status == -EPERM) { assert(status); } return status; } int ddr_config_handoff(handoff *hoff_ptr) { /* Populate DDR handoff data */ /* TODO: To add in DDR handoff configuration once available */ return 0; } // DDR firewall and non secure access void ddr_enable_ns_access(void) { /* Please set the ddr non secure registers accordingly */ mmio_setbits_32(CCU_REG(DMI0_DMIUSMCTCR), CCU_DMI_ALLOCEN | CCU_DMI_LOOKUPEN); mmio_setbits_32(CCU_REG(DMI1_DMIUSMCTCR), CCU_DMI_ALLOCEN | CCU_DMI_LOOKUPEN); /* TODO: To add in CCU NCORE OCRAM bypass mask for non secure registers */ NOTICE("DDR non secure configured\n"); } void ddr_enable_firewall(void) { /* Please set the ddr firewall registers accordingly */ /* TODO: To add in CCU NCORE OCRAM bypass mask for firewall registers */ NOTICE("DDR firewall enabled\n"); } bool is_ddr_init_in_progress(void) { uint32_t reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0)); if (reg & SOCFPGA_SYSMGR_BOOT_SCRATCH_POR_0_MASK) { return true; } return false; } int ddr_init(void) { // DDR driver initialization int status = -EPERM; uint32_t cmd_opcode = 0; // Check and set Boot Scratch Register if (is_ddr_init_in_progress()) { return status; } mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0), 0x01); // Populate DDR handoff data handoff reverse_handoff_ptr; if (!socfpga_get_handoff(&reverse_handoff_ptr)) { assert(status); } status = ddr_config_handoff(&reverse_handoff_ptr); if (status == -EPERM) { assert(status); } // CCU and firewall setup ddr_enable_ns_access(); ddr_enable_firewall(); // DDR calibration check status = ddr_calibration_check(); if (status == -EPERM) { assert(status); } // DDR mailbox command status = ddr_iossm_mailbox_cmd(cmd_opcode); if (status != 0) { assert(status); } // Check and set Boot Scratch Register mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0), 0x00); NOTICE("DDR init successfully\n"); return status; } int ddr_config_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type) { uint32_t temp[9] = {0}; int ret = 0; /* Write default value to prevent scrubber stop due to lower power */ mmio_write_32(0, umctl2_base + DDR4_PWRCTL_OFFSET); /* To backup user configurations in temp array */ temp[0] = mmio_read_32(umctl2_base + DDR4_SBRCTL_OFFSET); temp[1] = mmio_read_32(umctl2_base + DDR4_SBRWDATA0_OFFSET); temp[2] = mmio_read_32(umctl2_base + DDR4_SBRSTART0_OFFSET); if (umctl2_type == DDR_TYPE_DDR4) { temp[3] = mmio_read_32(umctl2_base + DDR4_SBRWDATA1_OFFSET); temp[4] = mmio_read_32(umctl2_base + DDR4_SBRSTART1_OFFSET); } temp[5] = mmio_read_32(umctl2_base + DDR4_SBRRANGE0_OFFSET); temp[6] = mmio_read_32(umctl2_base + DDR4_SBRRANGE1_OFFSET); temp[7] = mmio_read_32(umctl2_base + DDR4_ECCCFG0_OFFSET); temp[8] = mmio_read_32(umctl2_base + DDR4_ECCCFG1_OFFSET); if (umctl2_type != DDR_TYPE_DDR4) { /* Lock ECC region, ensure this regions is not being accessed */ mmio_setbits_32(umctl2_base + DDR4_ECCCFG1_OFFSET, LPDDR4_ECCCFG1_ECC_REGIONS_PARITY_LOCK); } /* Disable input traffic per port */ mmio_clrbits_32(umctl2_base + DDR4_PCTRL0_OFFSET, DDR4_PCTRL0_PORT_EN); /* Disables scrubber */ mmio_clrbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); /* Polling all scrub writes data have been sent */ ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), DDR4_SBRSTAT_SCRUB_BUSY, true, 5000); if (ret) { INFO("%s: Timeout while waiting for", __func__); INFO(" sending all scrub data\n"); return ret; } /* LPDDR4 supports inline ECC only */ if (umctl2_type != DDR_TYPE_DDR4) { /* * Setting all regions for protected, this is required for * srubber to init whole LPDDR4 expect ECC region */ mmio_write_32(((ONE_EIGHT << LPDDR4_ECCCFG0_ECC_REGION_MAP_GRANU_SHIFT) | (ALL_PROTECTED << LPDDR4_ECCCFG0_ECC_REGION_MAP_SHIFT)), umctl2_base + DDR4_ECCCFG0_OFFSET); } /* Scrub_burst = 1, scrub_mode = 1(performs writes) */ mmio_write_32(DDR4_SBRCTL_SCRUB_BURST_1 | DDR4_SBRCTL_SCRUB_WRITE, umctl2_base + DDR4_SBRCTL_OFFSET); /* Wipe DDR content after calibration */ ret = ddr_zerofill_scrubber(umctl2_base, umctl2_type); if (ret) { ERROR("Failed to clear DDR content\n"); } /* Polling all scrub writes data have been sent */ ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), DDR4_SBRSTAT_SCRUB_BUSY, true, 5000); if (ret) { INFO("%s: Timeout while waiting for", __func__); INFO(" sending all scrub data\n"); return ret; } /* Disables scrubber */ mmio_clrbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); /* Restore user configurations */ mmio_write_32(temp[0], umctl2_base + DDR4_SBRCTL_OFFSET); mmio_write_32(temp[1], umctl2_base + DDR4_SBRWDATA0_OFFSET); mmio_write_32(temp[2], umctl2_base + DDR4_SBRSTART0_OFFSET); if (umctl2_type == DDR_TYPE_DDR4) { mmio_write_32(temp[3], umctl2_base + DDR4_SBRWDATA1_OFFSET); mmio_write_32(temp[4], umctl2_base + DDR4_SBRSTART1_OFFSET); } mmio_write_32(temp[5], umctl2_base + DDR4_SBRRANGE0_OFFSET); mmio_write_32(temp[6], umctl2_base + DDR4_SBRRANGE1_OFFSET); mmio_write_32(temp[7], umctl2_base + DDR4_ECCCFG0_OFFSET); mmio_write_32(temp[8], umctl2_base + DDR4_ECCCFG1_OFFSET); /* Enables ECC scrub on scrubber */ if (!(mmio_read_32(umctl2_base + DDR4_SBRCTL_OFFSET) & DDR4_SBRCTL_SCRUB_WRITE)) { /* Enables scrubber */ mmio_setbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); } return 0; } int ddr_zerofill_scrubber(phys_addr_t umctl2_base, enum ddr_type umctl2_type) { int ret = 0; /* Zeroing whole DDR */ mmio_write_32(0, umctl2_base + DDR4_SBRWDATA0_OFFSET); mmio_write_32(0, umctl2_base + DDR4_SBRSTART0_OFFSET); if (umctl2_type == DDR_TYPE_DDR4) { mmio_write_32(0, umctl2_base + DDR4_SBRWDATA1_OFFSET); mmio_write_32(0, umctl2_base + DDR4_SBRSTART1_OFFSET); } mmio_write_32(0, umctl2_base + DDR4_SBRRANGE0_OFFSET); mmio_write_32(0, umctl2_base + DDR4_SBRRANGE1_OFFSET); NOTICE("Enabling scrubber (zeroing whole DDR) ...\n"); /* Enables scrubber */ mmio_setbits_32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN); /* Polling all scrub writes commands have been sent */ ret = poll_idle_status((umctl2_base + DDR4_SBRSTAT_OFFSET), DDR4_SBRSTAT_SCRUB_DONE, true, 5000); if (ret) { INFO("%s: Timeout while waiting for", __func__); INFO(" sending all scrub commands\n"); return ret; } return 0; } int poll_idle_status(uint32_t addr, uint32_t mask, uint32_t match, uint32_t delay_ms) { int time_out = delay_ms; while (time_out-- > 0) { if ((mmio_read_32(addr) & mask) == match) { return 0; } udelay(1000); } return -ETIMEDOUT; }