| // SPDX-License-Identifier: GPL-2.0 |
| |
| /* Copyright (C) 2023-2024 Linaro Ltd. */ |
| |
| #include <linux/io.h> |
| #include <linux/platform_device.h> |
| |
| #include "gsi.h" |
| #include "gsi_reg.h" |
| #include "reg.h" |
| |
| /* Is this register ID valid for the current GSI version? */ |
| static bool gsi_reg_id_valid(struct gsi *gsi, enum gsi_reg_id reg_id) |
| { |
| switch (reg_id) { |
| case INTER_EE_SRC_CH_IRQ_MSK: |
| case INTER_EE_SRC_EV_CH_IRQ_MSK: |
| return gsi->version >= IPA_VERSION_3_5; |
| |
| case HW_PARAM_2: |
| return gsi->version >= IPA_VERSION_3_5_1; |
| |
| case HW_PARAM_4: |
| return gsi->version >= IPA_VERSION_5_0; |
| |
| case CH_C_CNTXT_0: |
| case CH_C_CNTXT_1: |
| case CH_C_CNTXT_2: |
| case CH_C_CNTXT_3: |
| case CH_C_QOS: |
| case CH_C_SCRATCH_0: |
| case CH_C_SCRATCH_1: |
| case CH_C_SCRATCH_2: |
| case CH_C_SCRATCH_3: |
| case EV_CH_E_CNTXT_0: |
| case EV_CH_E_CNTXT_1: |
| case EV_CH_E_CNTXT_2: |
| case EV_CH_E_CNTXT_3: |
| case EV_CH_E_CNTXT_4: |
| case EV_CH_E_CNTXT_8: |
| case EV_CH_E_CNTXT_9: |
| case EV_CH_E_CNTXT_10: |
| case EV_CH_E_CNTXT_11: |
| case EV_CH_E_CNTXT_12: |
| case EV_CH_E_CNTXT_13: |
| case EV_CH_E_SCRATCH_0: |
| case EV_CH_E_SCRATCH_1: |
| case CH_C_DOORBELL_0: |
| case EV_CH_E_DOORBELL_0: |
| case GSI_STATUS: |
| case CH_CMD: |
| case EV_CH_CMD: |
| case GENERIC_CMD: |
| case CNTXT_TYPE_IRQ: |
| case CNTXT_TYPE_IRQ_MSK: |
| case CNTXT_SRC_CH_IRQ: |
| case CNTXT_SRC_CH_IRQ_MSK: |
| case CNTXT_SRC_CH_IRQ_CLR: |
| case CNTXT_SRC_EV_CH_IRQ: |
| case CNTXT_SRC_EV_CH_IRQ_MSK: |
| case CNTXT_SRC_EV_CH_IRQ_CLR: |
| case CNTXT_SRC_IEOB_IRQ: |
| case CNTXT_SRC_IEOB_IRQ_MSK: |
| case CNTXT_SRC_IEOB_IRQ_CLR: |
| case CNTXT_GLOB_IRQ_STTS: |
| case CNTXT_GLOB_IRQ_EN: |
| case CNTXT_GLOB_IRQ_CLR: |
| case CNTXT_GSI_IRQ_STTS: |
| case CNTXT_GSI_IRQ_EN: |
| case CNTXT_GSI_IRQ_CLR: |
| case CNTXT_INTSET: |
| case ERROR_LOG: |
| case ERROR_LOG_CLR: |
| case CNTXT_SCRATCH_0: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| const struct reg *gsi_reg(struct gsi *gsi, enum gsi_reg_id reg_id) |
| { |
| if (WARN(!gsi_reg_id_valid(gsi, reg_id), "invalid reg %u\n", reg_id)) |
| return NULL; |
| |
| return reg(gsi->regs, reg_id); |
| } |
| |
| static const struct regs *gsi_regs(struct gsi *gsi) |
| { |
| switch (gsi->version) { |
| case IPA_VERSION_3_1: |
| return &gsi_regs_v3_1; |
| |
| case IPA_VERSION_3_5_1: |
| return &gsi_regs_v3_5_1; |
| |
| case IPA_VERSION_4_2: |
| return &gsi_regs_v4_0; |
| |
| case IPA_VERSION_4_5: |
| case IPA_VERSION_4_7: |
| return &gsi_regs_v4_5; |
| |
| case IPA_VERSION_4_9: |
| return &gsi_regs_v4_9; |
| |
| case IPA_VERSION_4_11: |
| return &gsi_regs_v4_11; |
| |
| case IPA_VERSION_5_0: |
| case IPA_VERSION_5_5: |
| return &gsi_regs_v5_0; |
| |
| default: |
| return NULL; |
| } |
| } |
| |
| /* Sets gsi->virt and I/O maps the "gsi" memory range for registers */ |
| int gsi_reg_init(struct gsi *gsi, struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct resource *res; |
| resource_size_t size; |
| |
| /* Get GSI memory range and map it */ |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsi"); |
| if (!res) { |
| dev_err(dev, "DT error getting \"gsi\" memory property\n"); |
| return -ENODEV; |
| } |
| |
| size = resource_size(res); |
| if (res->start > U32_MAX || size > U32_MAX - res->start) { |
| dev_err(dev, "DT memory resource \"gsi\" out of range\n"); |
| return -EINVAL; |
| } |
| |
| gsi->regs = gsi_regs(gsi); |
| if (!gsi->regs) { |
| dev_err(dev, "unsupported IPA version %u (?)\n", gsi->version); |
| return -EINVAL; |
| } |
| |
| gsi->virt = ioremap(res->start, size); |
| if (!gsi->virt) { |
| dev_err(dev, "unable to remap \"gsi\" memory\n"); |
| return -ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| /* Inverse of gsi_reg_init() */ |
| void gsi_reg_exit(struct gsi *gsi) |
| { |
| iounmap(gsi->virt); |
| gsi->virt = NULL; |
| gsi->regs = NULL; |
| } |