18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2018 MediaTek Inc. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/completion.h> 68c2ecf20Sopenharmony_ci#include <linux/errno.h> 78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 108c2ecf20Sopenharmony_ci#include <linux/soc/mediatek/mtk-cmdq.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define CMDQ_WRITE_ENABLE_MASK BIT(0) 138c2ecf20Sopenharmony_ci#define CMDQ_POLL_ENABLE_MASK BIT(0) 148c2ecf20Sopenharmony_ci#define CMDQ_EOC_IRQ_EN BIT(0) 158c2ecf20Sopenharmony_ci#define CMDQ_REG_TYPE 1 168c2ecf20Sopenharmony_ci#define CMDQ_JUMP_RELATIVE 1 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct cmdq_instruction { 198c2ecf20Sopenharmony_ci union { 208c2ecf20Sopenharmony_ci u32 value; 218c2ecf20Sopenharmony_ci u32 mask; 228c2ecf20Sopenharmony_ci struct { 238c2ecf20Sopenharmony_ci u16 arg_c; 248c2ecf20Sopenharmony_ci u16 src_reg; 258c2ecf20Sopenharmony_ci }; 268c2ecf20Sopenharmony_ci }; 278c2ecf20Sopenharmony_ci union { 288c2ecf20Sopenharmony_ci u16 offset; 298c2ecf20Sopenharmony_ci u16 event; 308c2ecf20Sopenharmony_ci u16 reg_dst; 318c2ecf20Sopenharmony_ci }; 328c2ecf20Sopenharmony_ci union { 338c2ecf20Sopenharmony_ci u8 subsys; 348c2ecf20Sopenharmony_ci struct { 358c2ecf20Sopenharmony_ci u8 sop:5; 368c2ecf20Sopenharmony_ci u8 arg_c_t:1; 378c2ecf20Sopenharmony_ci u8 src_t:1; 388c2ecf20Sopenharmony_ci u8 dst_t:1; 398c2ecf20Sopenharmony_ci }; 408c2ecf20Sopenharmony_ci }; 418c2ecf20Sopenharmony_ci u8 op; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciint cmdq_dev_get_client_reg(struct device *dev, 458c2ecf20Sopenharmony_ci struct cmdq_client_reg *client_reg, int idx) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct of_phandle_args spec; 488c2ecf20Sopenharmony_ci int err; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (!client_reg) 518c2ecf20Sopenharmony_ci return -ENOENT; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci err = of_parse_phandle_with_fixed_args(dev->of_node, 548c2ecf20Sopenharmony_ci "mediatek,gce-client-reg", 558c2ecf20Sopenharmony_ci 3, idx, &spec); 568c2ecf20Sopenharmony_ci if (err < 0) { 578c2ecf20Sopenharmony_ci dev_err(dev, 588c2ecf20Sopenharmony_ci "error %d can't parse gce-client-reg property (%d)", 598c2ecf20Sopenharmony_ci err, idx); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return err; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci client_reg->subsys = (u8)spec.args[0]; 658c2ecf20Sopenharmony_ci client_reg->offset = (u16)spec.args[1]; 668c2ecf20Sopenharmony_ci client_reg->size = (u16)spec.args[2]; 678c2ecf20Sopenharmony_ci of_node_put(spec.np); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_dev_get_client_reg); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void cmdq_client_timeout(struct timer_list *t) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct cmdq_client *client = from_timer(client, t, timer); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci dev_err(client->client.dev, "cmdq timeout!\n"); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct cmdq_client *cmdq_mbox_create(struct device *dev, int index, u32 timeout) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct cmdq_client *client; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci client = kzalloc(sizeof(*client), GFP_KERNEL); 858c2ecf20Sopenharmony_ci if (!client) 868c2ecf20Sopenharmony_ci return (struct cmdq_client *)-ENOMEM; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci client->timeout_ms = timeout; 898c2ecf20Sopenharmony_ci if (timeout != CMDQ_NO_TIMEOUT) { 908c2ecf20Sopenharmony_ci spin_lock_init(&client->lock); 918c2ecf20Sopenharmony_ci timer_setup(&client->timer, cmdq_client_timeout, 0); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci client->pkt_cnt = 0; 948c2ecf20Sopenharmony_ci client->client.dev = dev; 958c2ecf20Sopenharmony_ci client->client.tx_block = false; 968c2ecf20Sopenharmony_ci client->client.knows_txdone = true; 978c2ecf20Sopenharmony_ci client->chan = mbox_request_channel(&client->client, index); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (IS_ERR(client->chan)) { 1008c2ecf20Sopenharmony_ci long err; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dev_err(dev, "failed to request channel\n"); 1038c2ecf20Sopenharmony_ci err = PTR_ERR(client->chan); 1048c2ecf20Sopenharmony_ci kfree(client); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ERR_PTR(err); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return client; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_mbox_create); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid cmdq_mbox_destroy(struct cmdq_client *client) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (client->timeout_ms != CMDQ_NO_TIMEOUT) { 1168c2ecf20Sopenharmony_ci spin_lock(&client->lock); 1178c2ecf20Sopenharmony_ci del_timer_sync(&client->timer); 1188c2ecf20Sopenharmony_ci spin_unlock(&client->lock); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci mbox_free_channel(client->chan); 1218c2ecf20Sopenharmony_ci kfree(client); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_mbox_destroy); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct cmdq_pkt *pkt; 1288c2ecf20Sopenharmony_ci struct device *dev; 1298c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); 1328c2ecf20Sopenharmony_ci if (!pkt) 1338c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1348c2ecf20Sopenharmony_ci pkt->va_base = kzalloc(size, GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!pkt->va_base) { 1368c2ecf20Sopenharmony_ci kfree(pkt); 1378c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci pkt->buf_size = size; 1408c2ecf20Sopenharmony_ci pkt->cl = (void *)client; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci dev = client->chan->mbox->dev; 1438c2ecf20Sopenharmony_ci dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, 1448c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1458c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 1468c2ecf20Sopenharmony_ci dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); 1478c2ecf20Sopenharmony_ci kfree(pkt->va_base); 1488c2ecf20Sopenharmony_ci kfree(pkt); 1498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci pkt->pa_base = dma_addr; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return pkt; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_create); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid cmdq_pkt_destroy(struct cmdq_pkt *pkt) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct cmdq_client *client = (struct cmdq_client *)pkt->cl; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, 1638c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1648c2ecf20Sopenharmony_ci kfree(pkt->va_base); 1658c2ecf20Sopenharmony_ci kfree(pkt); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_destroy); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int cmdq_pkt_append_command(struct cmdq_pkt *pkt, 1708c2ecf20Sopenharmony_ci struct cmdq_instruction inst) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct cmdq_instruction *cmd_ptr; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) { 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * In the case of allocated buffer size (pkt->buf_size) is used 1778c2ecf20Sopenharmony_ci * up, the real required size (pkt->cmdq_buf_size) is still 1788c2ecf20Sopenharmony_ci * increased, so that the user knows how much memory should be 1798c2ecf20Sopenharmony_ci * ultimately allocated after appending all commands and 1808c2ecf20Sopenharmony_ci * flushing the command packet. Therefor, the user can call 1818c2ecf20Sopenharmony_ci * cmdq_pkt_create() again with the real required buffer size. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci pkt->cmd_buf_size += CMDQ_INST_SIZE; 1848c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s: buffer size %u is too small !\n", 1858c2ecf20Sopenharmony_ci __func__, (u32)pkt->buf_size); 1868c2ecf20Sopenharmony_ci return -ENOMEM; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci cmd_ptr = pkt->va_base + pkt->cmd_buf_size; 1908c2ecf20Sopenharmony_ci *cmd_ptr = inst; 1918c2ecf20Sopenharmony_ci pkt->cmd_buf_size += CMDQ_INST_SIZE; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ciint cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct cmdq_instruction inst; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WRITE; 2018c2ecf20Sopenharmony_ci inst.value = value; 2028c2ecf20Sopenharmony_ci inst.offset = offset; 2038c2ecf20Sopenharmony_ci inst.subsys = subsys; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciint cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, 2108c2ecf20Sopenharmony_ci u16 offset, u32 value, u32 mask) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 2138c2ecf20Sopenharmony_ci u16 offset_mask = offset; 2148c2ecf20Sopenharmony_ci int err; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (mask != 0xffffffff) { 2178c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_MASK; 2188c2ecf20Sopenharmony_ci inst.mask = ~mask; 2198c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 2208c2ecf20Sopenharmony_ci if (err < 0) 2218c2ecf20Sopenharmony_ci return err; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci offset_mask |= CMDQ_WRITE_ENABLE_MASK; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci err = cmdq_pkt_write(pkt, subsys, offset_mask, value); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return err; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write_mask); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ciint cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, 2328c2ecf20Sopenharmony_ci u16 reg_idx) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_READ_S; 2378c2ecf20Sopenharmony_ci inst.dst_t = CMDQ_REG_TYPE; 2388c2ecf20Sopenharmony_ci inst.sop = high_addr_reg_idx; 2398c2ecf20Sopenharmony_ci inst.reg_dst = reg_idx; 2408c2ecf20Sopenharmony_ci inst.src_reg = addr_low; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_read_s); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciint cmdq_pkt_write_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, 2478c2ecf20Sopenharmony_ci u16 addr_low, u16 src_reg_idx) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WRITE_S; 2528c2ecf20Sopenharmony_ci inst.src_t = CMDQ_REG_TYPE; 2538c2ecf20Sopenharmony_ci inst.sop = high_addr_reg_idx; 2548c2ecf20Sopenharmony_ci inst.offset = addr_low; 2558c2ecf20Sopenharmony_ci inst.src_reg = src_reg_idx; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write_s); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ciint cmdq_pkt_write_s_mask(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, 2628c2ecf20Sopenharmony_ci u16 addr_low, u16 src_reg_idx, u32 mask) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 2658c2ecf20Sopenharmony_ci int err; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_MASK; 2688c2ecf20Sopenharmony_ci inst.mask = ~mask; 2698c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 2708c2ecf20Sopenharmony_ci if (err < 0) 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci inst.mask = 0; 2748c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WRITE_S_MASK; 2758c2ecf20Sopenharmony_ci inst.src_t = CMDQ_REG_TYPE; 2768c2ecf20Sopenharmony_ci inst.sop = high_addr_reg_idx; 2778c2ecf20Sopenharmony_ci inst.offset = addr_low; 2788c2ecf20Sopenharmony_ci inst.src_reg = src_reg_idx; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write_s_mask); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciint cmdq_pkt_write_s_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx, 2858c2ecf20Sopenharmony_ci u16 addr_low, u32 value) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WRITE_S; 2908c2ecf20Sopenharmony_ci inst.sop = high_addr_reg_idx; 2918c2ecf20Sopenharmony_ci inst.offset = addr_low; 2928c2ecf20Sopenharmony_ci inst.value = value; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write_s_value); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ciint cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx, 2998c2ecf20Sopenharmony_ci u16 addr_low, u32 value, u32 mask) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 3028c2ecf20Sopenharmony_ci int err; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_MASK; 3058c2ecf20Sopenharmony_ci inst.mask = ~mask; 3068c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 3078c2ecf20Sopenharmony_ci if (err < 0) 3088c2ecf20Sopenharmony_ci return err; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WRITE_S_MASK; 3118c2ecf20Sopenharmony_ci inst.sop = high_addr_reg_idx; 3128c2ecf20Sopenharmony_ci inst.offset = addr_low; 3138c2ecf20Sopenharmony_ci inst.value = value; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_write_s_mask_value); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 3228c2ecf20Sopenharmony_ci u32 clear_option = clear ? CMDQ_WFE_UPDATE : 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (event >= CMDQ_MAX_EVENT) 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WFE; 3288c2ecf20Sopenharmony_ci inst.value = CMDQ_WFE_OPTION | clear_option; 3298c2ecf20Sopenharmony_ci inst.event = event; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_wfe); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciint cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (event >= CMDQ_MAX_EVENT) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WFE; 3438c2ecf20Sopenharmony_ci inst.value = CMDQ_WFE_UPDATE; 3448c2ecf20Sopenharmony_ci inst.event = event; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_clear_event); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciint cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (event >= CMDQ_MAX_EVENT) 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_WFE; 3588c2ecf20Sopenharmony_ci inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE; 3598c2ecf20Sopenharmony_ci inst.event = event; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_set_event); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciint cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys, 3668c2ecf20Sopenharmony_ci u16 offset, u32 value) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 3698c2ecf20Sopenharmony_ci int err; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_POLL; 3728c2ecf20Sopenharmony_ci inst.value = value; 3738c2ecf20Sopenharmony_ci inst.offset = offset; 3748c2ecf20Sopenharmony_ci inst.subsys = subsys; 3758c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return err; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_poll); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciint cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys, 3828c2ecf20Sopenharmony_ci u16 offset, u32 value, u32 mask) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 3858c2ecf20Sopenharmony_ci int err; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_MASK; 3888c2ecf20Sopenharmony_ci inst.mask = ~mask; 3898c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 3908c2ecf20Sopenharmony_ci if (err < 0) 3918c2ecf20Sopenharmony_ci return err; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci offset = offset | CMDQ_POLL_ENABLE_MASK; 3948c2ecf20Sopenharmony_ci err = cmdq_pkt_poll(pkt, subsys, offset, value); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return err; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_poll_mask); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciint cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_LOGIC; 4058c2ecf20Sopenharmony_ci inst.dst_t = CMDQ_REG_TYPE; 4068c2ecf20Sopenharmony_ci inst.reg_dst = reg_idx; 4078c2ecf20Sopenharmony_ci inst.value = value; 4088c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_assign); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciint cmdq_pkt_jump(struct cmdq_pkt *pkt, dma_addr_t addr) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct cmdq_instruction inst = {}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_JUMP; 4178c2ecf20Sopenharmony_ci inst.offset = CMDQ_JUMP_RELATIVE; 4188c2ecf20Sopenharmony_ci inst.value = addr >> 4198c2ecf20Sopenharmony_ci cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan); 4208c2ecf20Sopenharmony_ci return cmdq_pkt_append_command(pkt, inst); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_jump); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciint cmdq_pkt_finalize(struct cmdq_pkt *pkt) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct cmdq_instruction inst = { {0} }; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* insert EOC and generate IRQ for each command iteration */ 4308c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_EOC; 4318c2ecf20Sopenharmony_ci inst.value = CMDQ_EOC_IRQ_EN; 4328c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 4338c2ecf20Sopenharmony_ci if (err < 0) 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* JUMP to end */ 4378c2ecf20Sopenharmony_ci inst.op = CMDQ_CODE_JUMP; 4388c2ecf20Sopenharmony_ci inst.value = CMDQ_JUMP_PASS >> 4398c2ecf20Sopenharmony_ci cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan); 4408c2ecf20Sopenharmony_ci err = cmdq_pkt_append_command(pkt, inst); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return err; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_finalize); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct cmdq_pkt *pkt = (struct cmdq_pkt *)data.data; 4498c2ecf20Sopenharmony_ci struct cmdq_task_cb *cb = &pkt->cb; 4508c2ecf20Sopenharmony_ci struct cmdq_client *client = (struct cmdq_client *)pkt->cl; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (client->timeout_ms != CMDQ_NO_TIMEOUT) { 4538c2ecf20Sopenharmony_ci unsigned long flags = 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 4568c2ecf20Sopenharmony_ci if (--client->pkt_cnt == 0) 4578c2ecf20Sopenharmony_ci del_timer(&client->timer); 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci mod_timer(&client->timer, jiffies + 4608c2ecf20Sopenharmony_ci msecs_to_jiffies(client->timeout_ms)); 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(client->chan->mbox->dev, pkt->pa_base, 4658c2ecf20Sopenharmony_ci pkt->cmd_buf_size, DMA_TO_DEVICE); 4668c2ecf20Sopenharmony_ci if (cb->cb) { 4678c2ecf20Sopenharmony_ci data.data = cb->data; 4688c2ecf20Sopenharmony_ci cb->cb(data); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ciint cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, 4738c2ecf20Sopenharmony_ci void *data) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int err; 4768c2ecf20Sopenharmony_ci unsigned long flags = 0; 4778c2ecf20Sopenharmony_ci struct cmdq_client *client = (struct cmdq_client *)pkt->cl; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci pkt->cb.cb = cb; 4808c2ecf20Sopenharmony_ci pkt->cb.data = data; 4818c2ecf20Sopenharmony_ci pkt->async_cb.cb = cmdq_pkt_flush_async_cb; 4828c2ecf20Sopenharmony_ci pkt->async_cb.data = pkt; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dma_sync_single_for_device(client->chan->mbox->dev, pkt->pa_base, 4858c2ecf20Sopenharmony_ci pkt->cmd_buf_size, DMA_TO_DEVICE); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (client->timeout_ms != CMDQ_NO_TIMEOUT) { 4888c2ecf20Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 4898c2ecf20Sopenharmony_ci if (client->pkt_cnt++ == 0) 4908c2ecf20Sopenharmony_ci mod_timer(&client->timer, jiffies + 4918c2ecf20Sopenharmony_ci msecs_to_jiffies(client->timeout_ms)); 4928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci err = mbox_send_message(client->chan, pkt); 4968c2ecf20Sopenharmony_ci if (err < 0) 4978c2ecf20Sopenharmony_ci return err; 4988c2ecf20Sopenharmony_ci /* We can send next packet immediately, so just call txdone. */ 4998c2ecf20Sopenharmony_ci mbox_client_txdone(client->chan, 0); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_flush_async); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistruct cmdq_flush_completion { 5068c2ecf20Sopenharmony_ci struct completion cmplt; 5078c2ecf20Sopenharmony_ci bool err; 5088c2ecf20Sopenharmony_ci}; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void cmdq_pkt_flush_cb(struct cmdq_cb_data data) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct cmdq_flush_completion *cmplt; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci cmplt = (struct cmdq_flush_completion *)data.data; 5158c2ecf20Sopenharmony_ci if (data.sta != CMDQ_CB_NORMAL) 5168c2ecf20Sopenharmony_ci cmplt->err = true; 5178c2ecf20Sopenharmony_ci else 5188c2ecf20Sopenharmony_ci cmplt->err = false; 5198c2ecf20Sopenharmony_ci complete(&cmplt->cmplt); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ciint cmdq_pkt_flush(struct cmdq_pkt *pkt) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct cmdq_flush_completion cmplt; 5258c2ecf20Sopenharmony_ci int err; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci init_completion(&cmplt.cmplt); 5288c2ecf20Sopenharmony_ci err = cmdq_pkt_flush_async(pkt, cmdq_pkt_flush_cb, &cmplt); 5298c2ecf20Sopenharmony_ci if (err < 0) 5308c2ecf20Sopenharmony_ci return err; 5318c2ecf20Sopenharmony_ci wait_for_completion(&cmplt.cmplt); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return cmplt.err ? -EFAULT : 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_pkt_flush); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 538