LCOV - code coverage report
Current view: top level - ipu4 - ipu6-fw-com.c (source / functions) Coverage Total Hit
Test: ipu4.info Lines: 94.2 % 121 114
Test Date: 2026-05-12 04:57:36 Functions: 100.0 % 10 10

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0-only
       2              : /*
       3              :  * Copyright (C) 2013 - 2023 Intel Corporation
       4              :  */
       5              : 
       6              : #include "ipu6.h"
       7              : #include "ipu6-bus.h"
       8              : #include "ipu6-dma.h"
       9              : #include "ipu6-fw-com.h"
      10              : 
      11              : /*
      12              :  * FWCOM layer is a shared resource between FW and driver. It consist
      13              :  * of token queues to both send and receive directions. Queue is simply
      14              :  * an array of structures with read and write indexes to the queue.
      15              :  * There are 1...n queues to both directions. Queues locates in
      16              :  * system RAM and are mapped to ISP MMU so that both CPU and ISP can
      17              :  * see the same buffer. Indexes are located in ISP DMEM so that FW code
      18              :  * can poll those with very low latency and cost. CPU access to indexes is
      19              :  * more costly but that happens only at message sending time and
      20              :  * interrupt triggered message handling. CPU doesn't need to poll indexes.
      21              :  * wr_reg / rd_reg are offsets to those dmem location. They are not
      22              :  * the indexes itself.
      23              :  */
      24              : 
      25              : /* Shared structure between driver and FW - do not modify */
      26              : struct ipu6_fw_sys_queue {
      27              :         u64 host_address;
      28              :         u32 vied_address;
      29              :         u32 size;
      30              :         u32 token_size;
      31              :         u32 wr_reg;     /* reg number in subsystem's regmem */
      32              :         u32 rd_reg;
      33              :         u32 _align;
      34              : } __packed;
      35              : 
      36              : struct ipu6_fw_sys_queue_res {
      37              :         u64 host_address;
      38              :         u32 vied_address;
      39              :         u32 reg;
      40              : } __packed;
      41              : 
      42              : enum syscom_state {
      43              :         /* Program load or explicit host setting should init to this */
      44              :         SYSCOM_STATE_UNINIT = 0x57A7E000,
      45              :         /* SP Syscom sets this when it is ready for use */
      46              :         SYSCOM_STATE_READY = 0x57A7E001,
      47              :         /* SP Syscom sets this when no more syscom accesses will happen */
      48              :         SYSCOM_STATE_INACTIVE = 0x57A7E002
      49              : };
      50              : 
      51              : enum syscom_cmd {
      52              :         /* Program load or explicit host setting should init to this */
      53              :         SYSCOM_COMMAND_UNINIT = 0x57A7F000,
      54              :         /* Host Syscom requests syscom to become inactive */
      55              :         SYSCOM_COMMAND_INACTIVE = 0x57A7F001
      56              : };
      57              : 
      58              : /* firmware config: data that sent from the host to SP via DDR */
      59              : /* Cell copies data into a context */
      60              : 
      61              : struct ipu6_fw_syscom_config {
      62              :         u32 firmware_address;
      63              : 
      64              :         u32 num_input_queues;
      65              :         u32 num_output_queues;
      66              : 
      67              :         /* ISP pointers to an array of ipu6_fw_sys_queue structures */
      68              :         u32 input_queue;
      69              :         u32 output_queue;
      70              : 
      71              :         /* ISYS / PSYS private data */
      72              :         u32 specific_addr;
      73              :         u32 specific_size;
      74              : };
      75              : 
      76              : struct ipu6_fw_com_context {
      77              :         struct ipu6_bus_device *adev;
      78              :         void __iomem *dmem_addr;
      79              :         int (*cell_ready)(struct ipu6_bus_device *adev);
      80              :         void (*cell_start)(struct ipu6_bus_device *adev);
      81              : 
      82              :         void *dma_buffer;
      83              :         dma_addr_t dma_addr;
      84              :         unsigned int dma_size;
      85              : 
      86              :         struct ipu6_fw_sys_queue *input_queue;  /* array of host to SP queues */
      87              :         struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */
      88              : 
      89              :         u32 config_vied_addr;
      90              : 
      91              :         unsigned int buttress_boot_offset;
      92              :         void __iomem *base_addr;
      93              : };
      94              : 
      95              : #define FW_COM_WR_REG 0
      96              : #define FW_COM_RD_REG 4
      97              : 
      98              : #define REGMEM_OFFSET 0
      99              : #define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
     100              : 
     101              : enum regmem_id {
     102              :         /* pass pkg_dir address to SPC in non-secure mode */
     103              :         PKG_DIR_ADDR_REG = 0,
     104              :         /* pass syscom configuration to SPC */
     105              :         SYSCOM_CONFIG_REG = 1,
     106              :         /* syscom state - modified by SP */
     107              :         SYSCOM_STATE_REG = 2,
     108              :         /* syscom commands - modified by the host */
     109              :         SYSCOM_COMMAND_REG = 3,
     110              :         /* Store interrupt status - updated by SP */
     111              :         SYSCOM_IRQ_REG = 4,
     112              :         /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/
     113              :         SYSCOM_VTL0_ADDR_MASK = 5,
     114              :         /* first syscom queue pointer register */
     115              :         SYSCOM_QPR_BASE_REG = 6
     116              : };
     117              : 
     118              : enum message_direction {
     119              :         DIR_RECV = 0,
     120              :         DIR_SEND
     121              : };
     122              : 
     123              : #define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
     124              : #define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id)                    \
     125              :         ((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
     126              : 
     127              : enum buttress_syscom_id {
     128              :         /* pass syscom configuration to SPC */
     129              :         SYSCOM_CONFIG_ID                = 0,
     130              :         /* syscom state - modified by SP */
     131              :         SYSCOM_STATE_ID                 = 1,
     132              :         /* syscom vtl0 addr mask */
     133              :         SYSCOM_VTL0_ADDR_MASK_ID        = 2,
     134              :         SYSCOM_ID_MAX
     135              : };
     136              : 
     137           12 : static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
     138              :                                 unsigned int token_size,
     139              :                                 struct ipu6_fw_sys_queue_res *res)
     140              : {
     141           12 :         unsigned int buf_size = (size + 1) * token_size;
     142              : 
     143           12 :         q->size = size + 1;
     144           12 :         q->token_size = token_size;
     145              : 
     146              :         /* acquire the shared buffer space */
     147           12 :         q->host_address = res->host_address;
     148           12 :         res->host_address += buf_size;
     149           12 :         q->vied_address = res->vied_address;
     150           12 :         res->vied_address += buf_size;
     151              : 
     152              :         /* acquire the shared read and writer pointers */
     153           12 :         q->wr_reg = res->reg;
     154           12 :         res->reg++;
     155           12 :         q->rd_reg = res->reg;
     156           12 :         res->reg++;
     157           12 : }
     158              : 
     159            1 : struct ipu6_fw_com_context *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
     160              :                                                 struct ipu6_bus_device *adev,
     161              :                                                 void __iomem *base)
     162              : {
     163              :         size_t conf_size, inq_size, outq_size, specific_size;
     164              :         struct ipu6_fw_syscom_config *config_host_addr;
     165              :         unsigned int sizeinput = 0, sizeoutput = 0;
     166            1 :         struct ipu6_fw_sys_queue_res res;
     167              :         struct ipu6_fw_com_context *ctx;
     168            1 :         struct device *dev = &adev->auxdev.dev;
     169              :         size_t sizeall, offset;
     170              :         void *specific_host_addr;
     171              :         unsigned int i;
     172              : 
     173            1 :         if (!cfg || !cfg->cell_start || !cfg->cell_ready)
     174              :                 return NULL;
     175              : 
     176            1 :         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
     177            1 :         if (!ctx)
     178              :                 return NULL;
     179            1 :         ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
     180            1 :         ctx->adev = adev;
     181            1 :         ctx->cell_start = cfg->cell_start;
     182            1 :         ctx->cell_ready = cfg->cell_ready;
     183            1 :         ctx->buttress_boot_offset = cfg->buttress_boot_offset;
     184            1 :         ctx->base_addr  = base;
     185              : 
     186              :         /*
     187              :          * Allocate DMA mapped memory. Allocate one big chunk.
     188              :          */
     189              :         /* Base cfg for FW */
     190              :         conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
     191              :         /* Descriptions of the queues */
     192            1 :         inq_size = size_mul(cfg->num_input_queues,
     193              :                             sizeof(struct ipu6_fw_sys_queue));
     194            1 :         outq_size = size_mul(cfg->num_output_queues,
     195              :                              sizeof(struct ipu6_fw_sys_queue));
     196              :         /* FW specific information structure */
     197            1 :         specific_size = roundup(cfg->specific_size, 8);
     198              : 
     199            1 :         sizeall = conf_size + inq_size + outq_size + specific_size;
     200              : 
     201           11 :         for (i = 0; i < cfg->num_input_queues; i++)
     202           10 :                 sizeinput += size_mul(cfg->input[i].queue_size + 1,
     203           10 :                                       cfg->input[i].token_size);
     204              : 
     205            3 :         for (i = 0; i < cfg->num_output_queues; i++)
     206            2 :                 sizeoutput += size_mul(cfg->output[i].queue_size + 1,
     207            2 :                                        cfg->output[i].token_size);
     208              : 
     209            1 :         sizeall += sizeinput + sizeoutput;
     210              : 
     211            1 :         ctx->dma_buffer = ipu6_dma_alloc(adev, sizeall, &ctx->dma_addr,
     212              :                                          GFP_KERNEL, 0);
     213            1 :         if (!ctx->dma_buffer) {
     214            0 :                 dev_err(dev, "failed to allocate dma memory\n");
     215            0 :                 kfree(ctx);
     216            0 :                 return NULL;
     217              :         }
     218              : 
     219            1 :         ctx->dma_size = sizeall;
     220              : 
     221              :         config_host_addr = ctx->dma_buffer;
     222            1 :         ctx->config_vied_addr = ctx->dma_addr;
     223              : 
     224              :         offset = conf_size;
     225            1 :         ctx->input_queue = ctx->dma_buffer + offset;
     226            1 :         config_host_addr->input_queue = ctx->dma_addr + offset;
     227            1 :         config_host_addr->num_input_queues = cfg->num_input_queues;
     228              : 
     229              :         offset += inq_size;
     230            1 :         ctx->output_queue = ctx->dma_buffer + offset;
     231            1 :         config_host_addr->output_queue = ctx->dma_addr + offset;
     232            1 :         config_host_addr->num_output_queues = cfg->num_output_queues;
     233              : 
     234              :         /* copy firmware specific data */
     235              :         offset += outq_size;
     236            1 :         specific_host_addr = ctx->dma_buffer + offset;
     237            1 :         config_host_addr->specific_addr = ctx->dma_addr + offset;
     238            1 :         config_host_addr->specific_size = cfg->specific_size;
     239            1 :         if (cfg->specific_addr && cfg->specific_size)
     240            1 :                 memcpy(specific_host_addr, cfg->specific_addr,
     241              :                        cfg->specific_size);
     242              : 
     243            1 :         ipu6_dma_sync_single(adev, ctx->config_vied_addr, sizeall);
     244              : 
     245              :         /* initialize input queues */
     246              :         offset += specific_size;
     247            1 :         res.reg = SYSCOM_QPR_BASE_REG;
     248            1 :         res.host_address = (uintptr_t)(ctx->dma_buffer + offset);
     249            1 :         res.vied_address = ctx->dma_addr + offset;
     250           11 :         for (i = 0; i < cfg->num_input_queues; i++)
     251           10 :                 ipu6_sys_queue_init(ctx->input_queue + i,
     252              :                                     cfg->input[i].queue_size,
     253           10 :                                     cfg->input[i].token_size, &res);
     254              : 
     255              :         /* initialize output queues */
     256            1 :         offset += sizeinput;
     257            1 :         res.host_address = (uintptr_t)(ctx->dma_buffer + offset);
     258            1 :         res.vied_address = ctx->dma_addr + offset;
     259            3 :         for (i = 0; i < cfg->num_output_queues; i++) {
     260            2 :                 ipu6_sys_queue_init(ctx->output_queue + i,
     261              :                                     cfg->output[i].queue_size,
     262            2 :                                     cfg->output[i].token_size, &res);
     263              :         }
     264              : 
     265              :         return ctx;
     266              : }
     267              : EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
     268              : 
     269            1 : int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
     270              : {
     271              :         /* Check if SP is in valid state */
     272            1 :         if (!ctx->cell_ready(ctx->adev))
     273              :                 return -EIO;
     274              : 
     275              :         /* store syscom uninitialized state */
     276            1 :         writel(SYSCOM_STATE_UNINIT, ctx->dmem_addr + SYSCOM_STATE_REG * 4);
     277              :         // NB: Re-ordered because that is how 4.19 does it
     278              :         // - not sure if it matters
     279              :         /* store syscom uninitialized command */
     280            1 :         writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
     281              : 
     282              :         /* store firmware configuration address */
     283            1 :         writel(ctx->config_vied_addr,
     284            1 :                ctx->dmem_addr + SYSCOM_CONFIG_REG * 4);
     285              : 
     286            1 :         ctx->cell_start(ctx->adev);
     287              : 
     288            1 :         return 0;
     289              : }
     290              : EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
     291              : 
     292            1 : int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
     293              : {
     294              :         int state;
     295              : 
     296            1 :         state = readl(ctx->dmem_addr + SYSCOM_STATE_REG * 4);
     297            1 :         if (state != SYSCOM_STATE_READY)
     298              :                 return -EBUSY;
     299              : 
     300              :         /* set close request flag */
     301            1 :         writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
     302              :                SYSCOM_COMMAND_REG * 4);
     303              : 
     304            1 :         return 0;
     305              : }
     306              : EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
     307              : 
     308            1 : int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
     309              : {
     310              :         /* check if release is forced, an verify cell state if it is not */
     311            1 :         if (!force && !ctx->cell_ready(ctx->adev))
     312              :                 return -EBUSY;
     313              : 
     314            1 :         ipu6_dma_free(ctx->adev, ctx->dma_size,
     315              :                       ctx->dma_buffer, ctx->dma_addr, 0);
     316            1 :         kfree(ctx);
     317            1 :         return 0;
     318              : }
     319              : EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
     320              : 
     321            1 : bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
     322              : {
     323              :         int state;
     324              : 
     325            1 :         state = readl(ctx->dmem_addr + SYSCOM_STATE_REG * 4);
     326              : 
     327            1 :         return state == SYSCOM_STATE_READY;
     328              : }
     329              : EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
     330              : 
     331            5 : void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
     332              : {
     333            5 :         struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
     334            5 :         void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
     335              :         unsigned int wr, rd;
     336              :         unsigned int packets;
     337              :         unsigned int index;
     338              : 
     339              :         wr = readl(q_dmem + FW_COM_WR_REG);
     340            5 :         rd = readl(q_dmem + FW_COM_RD_REG);
     341              : 
     342            5 :         if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
     343            0 :                 return NULL;
     344              : 
     345            5 :         if (wr < rd)
     346            0 :                 packets = rd - wr - 1;
     347              :         else
     348            5 :                 packets = q->size - (wr - rd + 1);
     349              : 
     350            5 :         if (!packets)
     351              :                 return NULL;
     352              : 
     353              :         index = readl(q_dmem + FW_COM_WR_REG);
     354              : 
     355            5 :         return (void *)((uintptr_t)q->host_address + index * q->token_size);
     356              : }
     357              : EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
     358              : 
     359            5 : void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
     360              : {
     361            5 :         struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
     362            5 :         void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
     363            5 :         unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
     364              : 
     365            5 :         if (wr >= q->size)
     366              :                 wr = 0;
     367              : 
     368              :         writel(wr, q_dmem + FW_COM_WR_REG);
     369            5 : }
     370              : EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
     371              : 
     372           13 : void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
     373              : {
     374           13 :         struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
     375           13 :         void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
     376              :         unsigned int wr, rd;
     377              :         unsigned int packets;
     378              : 
     379              :         wr = readl(q_dmem + FW_COM_WR_REG);
     380           13 :         rd = readl(q_dmem + FW_COM_RD_REG);
     381              : 
     382           13 :         if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
     383            0 :                 return NULL;
     384              : 
     385           13 :         if (wr < rd)
     386            0 :                 wr += q->size;
     387              : 
     388              :         packets = wr - rd;
     389           13 :         if (!packets)
     390              :                 return NULL;
     391              : 
     392            8 :         return (void *)((uintptr_t)q->host_address + rd * q->token_size);
     393              : }
     394              : EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
     395              : 
     396            8 : void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
     397              : {
     398            8 :         struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
     399            8 :         void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
     400              :         unsigned int rd;
     401              : 
     402            8 :         rd = readl(q_dmem + FW_COM_RD_REG) + 1;
     403              : 
     404            8 :         if (rd >= q->size)
     405              :                 rd = 0;
     406              : 
     407              :         writel(rd, q_dmem + FW_COM_RD_REG);
     408            8 : }
     409              : EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
        

Generated by: LCOV version 2.0-1