| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Structures and helpers for managing GXP MicroController Unit. |
| * |
| * Copyright (C) 2022 Google LLC |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/sizes.h> |
| |
| #include <gcip/gcip-mem-pool.h> |
| |
| #include "gxp-config.h" |
| #include "gxp-internal.h" |
| #include "gxp-kci.h" |
| #include "gxp-lpm.h" |
| #include "gxp-mcu-firmware.h" |
| #include "gxp-mcu-platform.h" |
| #include "gxp-mcu.h" |
| #include "gxp-uci.h" |
| |
| /* Allocates the MCU <-> cores shared buffer region. */ |
| static int gxp_alloc_shared_buffer(struct gxp_dev *gxp, struct gxp_mcu *mcu) |
| { |
| const size_t size = GXP_SHARED_BUFFER_SIZE; |
| phys_addr_t paddr; |
| struct gxp_mapped_resource *res = &mcu->gxp->shared_buf; |
| size_t offset; |
| void *vaddr; |
| |
| paddr = gcip_mem_pool_alloc(&mcu->remap_data_pool, size); |
| if (!paddr) |
| return -ENOMEM; |
| res->paddr = paddr; |
| res->size = size; |
| res->daddr = GXP_IOVA_SHARED_BUFFER; |
| |
| /* clear shared buffer to make sure it's clean */ |
| offset = gcip_mem_pool_offset(&mcu->remap_data_pool, paddr); |
| vaddr = offset + (mcu->fw.image_buf.vaddr + GXP_IREMAP_DATA_OFFSET); |
| memset(vaddr, 0, size); |
| res->vaddr = vaddr; |
| |
| return 0; |
| } |
| |
| static void gxp_free_shared_buffer(struct gxp_mcu *mcu) |
| { |
| struct gxp_mapped_resource *res = &mcu->gxp->shared_buf; |
| |
| gcip_mem_pool_free(&mcu->remap_data_pool, res->paddr, res->size); |
| } |
| |
| /* |
| * Initializes memory pools, must be called after @mcu->fw has been initialized |
| * to have a valid image_buf. |
| */ |
| static int gxp_mcu_mem_pools_init(struct gxp_dev *gxp, struct gxp_mcu *mcu) |
| { |
| phys_addr_t iremap_paddr = mcu->fw.image_buf.paddr; |
| int ret; |
| |
| ret = gcip_mem_pool_init(&mcu->remap_data_pool, gxp->dev, |
| iremap_paddr + GXP_IREMAP_DATA_OFFSET, |
| GXP_IREMAP_DATA_SIZE, SZ_4K); |
| if (ret) |
| return ret; |
| ret = gcip_mem_pool_init(&mcu->remap_secure_pool, gxp->dev, |
| iremap_paddr + GXP_IREMAP_SECURE_OFFSET, |
| GXP_IREMAP_SECURE_SIZE, SZ_4K); |
| if (ret) { |
| gcip_mem_pool_exit(&mcu->remap_data_pool); |
| return ret; |
| } |
| return 0; |
| } |
| |
| static void gxp_mcu_mem_pools_exit(struct gxp_mcu *mcu) |
| { |
| gcip_mem_pool_exit(&mcu->remap_secure_pool); |
| gcip_mem_pool_exit(&mcu->remap_data_pool); |
| } |
| |
| int gxp_mcu_mem_alloc_data(struct gxp_mcu *mcu, struct gxp_mapped_resource *mem, size_t size) |
| { |
| size_t offset; |
| phys_addr_t paddr; |
| |
| paddr = gcip_mem_pool_alloc(&mcu->remap_data_pool, size); |
| if (!paddr) |
| return -ENOMEM; |
| offset = gcip_mem_pool_offset(&mcu->remap_data_pool, paddr); |
| mem->size = size; |
| mem->paddr = paddr; |
| mem->vaddr = offset + (mcu->fw.image_buf.vaddr + GXP_IREMAP_DATA_OFFSET); |
| mem->daddr = offset + (mcu->fw.image_buf.daddr + GXP_IREMAP_DATA_OFFSET); |
| return 0; |
| } |
| |
| void gxp_mcu_mem_free_data(struct gxp_mcu *mcu, struct gxp_mapped_resource *mem) |
| { |
| gcip_mem_pool_free(&mcu->remap_data_pool, mem->paddr, mem->size); |
| mem->size = 0; |
| mem->paddr = 0; |
| mem->vaddr = NULL; |
| mem->daddr = 0; |
| } |
| |
| int gxp_mcu_init(struct gxp_dev *gxp, struct gxp_mcu *mcu) |
| { |
| int ret; |
| |
| mcu->gxp = gxp; |
| ret = gxp_mcu_firmware_init(gxp, &mcu->fw); |
| if (ret) |
| return ret; |
| ret = gxp_mcu_mem_pools_init(gxp, mcu); |
| if (ret) |
| goto err_fw_exit; |
| ret = gxp_alloc_shared_buffer(gxp, mcu); |
| if (ret) |
| goto err_pools_exit; |
| /* |
| * MCU telemetry must be initialized before UCI and KCI to match the |
| * .log_buffer address in the firmware linker.ld. |
| */ |
| ret = gxp_mcu_telemetry_init(mcu); |
| if (ret) |
| goto err_free_shared_buffer; |
| ret = gxp_uci_init(mcu); |
| if (ret) |
| goto err_telemetry_exit; |
| ret = gxp_kci_init(mcu); |
| if (ret) |
| goto err_uci_exit; |
| return 0; |
| |
| err_uci_exit: |
| gxp_uci_exit(&mcu->uci); |
| err_telemetry_exit: |
| gxp_mcu_telemetry_exit(mcu); |
| err_free_shared_buffer: |
| gxp_free_shared_buffer(mcu); |
| err_pools_exit: |
| gxp_mcu_mem_pools_exit(mcu); |
| err_fw_exit: |
| gxp_mcu_firmware_exit(&mcu->fw); |
| return ret; |
| } |
| |
| void gxp_mcu_exit(struct gxp_mcu *mcu) |
| { |
| gxp_kci_exit(&mcu->kci); |
| gxp_uci_exit(&mcu->uci); |
| gxp_mcu_telemetry_exit(mcu); |
| gxp_free_shared_buffer(mcu); |
| gxp_mcu_mem_pools_exit(mcu); |
| gxp_mcu_firmware_exit(&mcu->fw); |
| } |
| |
| |
| void gxp_mcu_reset_mailbox(struct gxp_mcu *mcu) |
| { |
| gxp_uci_reinit(&mcu->uci); |
| gxp_kci_reinit(&mcu->kci); |
| } |