brintos

brintos / linux-shallow public Read only

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