Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2018 MediaTek Inc.
0004 
0005 #include <linux/completion.h>
0006 #include <linux/errno.h>
0007 #include <linux/dma-mapping.h>
0008 #include <linux/module.h>
0009 #include <linux/mailbox_controller.h>
0010 #include <linux/soc/mediatek/mtk-cmdq.h>
0011 
0012 #define CMDQ_WRITE_ENABLE_MASK  BIT(0)
0013 #define CMDQ_POLL_ENABLE_MASK   BIT(0)
0014 #define CMDQ_EOC_IRQ_EN     BIT(0)
0015 #define CMDQ_REG_TYPE       1
0016 #define CMDQ_JUMP_RELATIVE  1
0017 
0018 struct cmdq_instruction {
0019     union {
0020         u32 value;
0021         u32 mask;
0022         struct {
0023             u16 arg_c;
0024             u16 src_reg;
0025         };
0026     };
0027     union {
0028         u16 offset;
0029         u16 event;
0030         u16 reg_dst;
0031     };
0032     union {
0033         u8 subsys;
0034         struct {
0035             u8 sop:5;
0036             u8 arg_c_t:1;
0037             u8 src_t:1;
0038             u8 dst_t:1;
0039         };
0040     };
0041     u8 op;
0042 };
0043 
0044 int cmdq_dev_get_client_reg(struct device *dev,
0045                 struct cmdq_client_reg *client_reg, int idx)
0046 {
0047     struct of_phandle_args spec;
0048     int err;
0049 
0050     if (!client_reg)
0051         return -ENOENT;
0052 
0053     err = of_parse_phandle_with_fixed_args(dev->of_node,
0054                            "mediatek,gce-client-reg",
0055                            3, idx, &spec);
0056     if (err < 0) {
0057         dev_err(dev,
0058             "error %d can't parse gce-client-reg property (%d)",
0059             err, idx);
0060 
0061         return err;
0062     }
0063 
0064     client_reg->subsys = (u8)spec.args[0];
0065     client_reg->offset = (u16)spec.args[1];
0066     client_reg->size = (u16)spec.args[2];
0067     of_node_put(spec.np);
0068 
0069     return 0;
0070 }
0071 EXPORT_SYMBOL(cmdq_dev_get_client_reg);
0072 
0073 struct cmdq_client *cmdq_mbox_create(struct device *dev, int index)
0074 {
0075     struct cmdq_client *client;
0076 
0077     client = kzalloc(sizeof(*client), GFP_KERNEL);
0078     if (!client)
0079         return (struct cmdq_client *)-ENOMEM;
0080 
0081     client->client.dev = dev;
0082     client->client.tx_block = false;
0083     client->client.knows_txdone = true;
0084     client->chan = mbox_request_channel(&client->client, index);
0085 
0086     if (IS_ERR(client->chan)) {
0087         long err;
0088 
0089         dev_err(dev, "failed to request channel\n");
0090         err = PTR_ERR(client->chan);
0091         kfree(client);
0092 
0093         return ERR_PTR(err);
0094     }
0095 
0096     return client;
0097 }
0098 EXPORT_SYMBOL(cmdq_mbox_create);
0099 
0100 void cmdq_mbox_destroy(struct cmdq_client *client)
0101 {
0102     mbox_free_channel(client->chan);
0103     kfree(client);
0104 }
0105 EXPORT_SYMBOL(cmdq_mbox_destroy);
0106 
0107 struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size)
0108 {
0109     struct cmdq_pkt *pkt;
0110     struct device *dev;
0111     dma_addr_t dma_addr;
0112 
0113     pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
0114     if (!pkt)
0115         return ERR_PTR(-ENOMEM);
0116     pkt->va_base = kzalloc(size, GFP_KERNEL);
0117     if (!pkt->va_base) {
0118         kfree(pkt);
0119         return ERR_PTR(-ENOMEM);
0120     }
0121     pkt->buf_size = size;
0122     pkt->cl = (void *)client;
0123 
0124     dev = client->chan->mbox->dev;
0125     dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
0126                   DMA_TO_DEVICE);
0127     if (dma_mapping_error(dev, dma_addr)) {
0128         dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
0129         kfree(pkt->va_base);
0130         kfree(pkt);
0131         return ERR_PTR(-ENOMEM);
0132     }
0133 
0134     pkt->pa_base = dma_addr;
0135 
0136     return pkt;
0137 }
0138 EXPORT_SYMBOL(cmdq_pkt_create);
0139 
0140 void cmdq_pkt_destroy(struct cmdq_pkt *pkt)
0141 {
0142     struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
0143 
0144     dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size,
0145              DMA_TO_DEVICE);
0146     kfree(pkt->va_base);
0147     kfree(pkt);
0148 }
0149 EXPORT_SYMBOL(cmdq_pkt_destroy);
0150 
0151 static int cmdq_pkt_append_command(struct cmdq_pkt *pkt,
0152                    struct cmdq_instruction inst)
0153 {
0154     struct cmdq_instruction *cmd_ptr;
0155 
0156     if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) {
0157         /*
0158          * In the case of allocated buffer size (pkt->buf_size) is used
0159          * up, the real required size (pkt->cmdq_buf_size) is still
0160          * increased, so that the user knows how much memory should be
0161          * ultimately allocated after appending all commands and
0162          * flushing the command packet. Therefor, the user can call
0163          * cmdq_pkt_create() again with the real required buffer size.
0164          */
0165         pkt->cmd_buf_size += CMDQ_INST_SIZE;
0166         WARN_ONCE(1, "%s: buffer size %u is too small !\n",
0167             __func__, (u32)pkt->buf_size);
0168         return -ENOMEM;
0169     }
0170 
0171     cmd_ptr = pkt->va_base + pkt->cmd_buf_size;
0172     *cmd_ptr = inst;
0173     pkt->cmd_buf_size += CMDQ_INST_SIZE;
0174 
0175     return 0;
0176 }
0177 
0178 int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
0179 {
0180     struct cmdq_instruction inst;
0181 
0182     inst.op = CMDQ_CODE_WRITE;
0183     inst.value = value;
0184     inst.offset = offset;
0185     inst.subsys = subsys;
0186 
0187     return cmdq_pkt_append_command(pkt, inst);
0188 }
0189 EXPORT_SYMBOL(cmdq_pkt_write);
0190 
0191 int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
0192             u16 offset, u32 value, u32 mask)
0193 {
0194     struct cmdq_instruction inst = { {0} };
0195     u16 offset_mask = offset;
0196     int err;
0197 
0198     if (mask != 0xffffffff) {
0199         inst.op = CMDQ_CODE_MASK;
0200         inst.mask = ~mask;
0201         err = cmdq_pkt_append_command(pkt, inst);
0202         if (err < 0)
0203             return err;
0204 
0205         offset_mask |= CMDQ_WRITE_ENABLE_MASK;
0206     }
0207     err = cmdq_pkt_write(pkt, subsys, offset_mask, value);
0208 
0209     return err;
0210 }
0211 EXPORT_SYMBOL(cmdq_pkt_write_mask);
0212 
0213 int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low,
0214             u16 reg_idx)
0215 {
0216     struct cmdq_instruction inst = {};
0217 
0218     inst.op = CMDQ_CODE_READ_S;
0219     inst.dst_t = CMDQ_REG_TYPE;
0220     inst.sop = high_addr_reg_idx;
0221     inst.reg_dst = reg_idx;
0222     inst.src_reg = addr_low;
0223 
0224     return cmdq_pkt_append_command(pkt, inst);
0225 }
0226 EXPORT_SYMBOL(cmdq_pkt_read_s);
0227 
0228 int cmdq_pkt_write_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx,
0229              u16 addr_low, u16 src_reg_idx)
0230 {
0231     struct cmdq_instruction inst = {};
0232 
0233     inst.op = CMDQ_CODE_WRITE_S;
0234     inst.src_t = CMDQ_REG_TYPE;
0235     inst.sop = high_addr_reg_idx;
0236     inst.offset = addr_low;
0237     inst.src_reg = src_reg_idx;
0238 
0239     return cmdq_pkt_append_command(pkt, inst);
0240 }
0241 EXPORT_SYMBOL(cmdq_pkt_write_s);
0242 
0243 int cmdq_pkt_write_s_mask(struct cmdq_pkt *pkt, u16 high_addr_reg_idx,
0244               u16 addr_low, u16 src_reg_idx, u32 mask)
0245 {
0246     struct cmdq_instruction inst = {};
0247     int err;
0248 
0249     inst.op = CMDQ_CODE_MASK;
0250     inst.mask = ~mask;
0251     err = cmdq_pkt_append_command(pkt, inst);
0252     if (err < 0)
0253         return err;
0254 
0255     inst.mask = 0;
0256     inst.op = CMDQ_CODE_WRITE_S_MASK;
0257     inst.src_t = CMDQ_REG_TYPE;
0258     inst.sop = high_addr_reg_idx;
0259     inst.offset = addr_low;
0260     inst.src_reg = src_reg_idx;
0261 
0262     return cmdq_pkt_append_command(pkt, inst);
0263 }
0264 EXPORT_SYMBOL(cmdq_pkt_write_s_mask);
0265 
0266 int cmdq_pkt_write_s_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx,
0267                u16 addr_low, u32 value)
0268 {
0269     struct cmdq_instruction inst = {};
0270 
0271     inst.op = CMDQ_CODE_WRITE_S;
0272     inst.sop = high_addr_reg_idx;
0273     inst.offset = addr_low;
0274     inst.value = value;
0275 
0276     return cmdq_pkt_append_command(pkt, inst);
0277 }
0278 EXPORT_SYMBOL(cmdq_pkt_write_s_value);
0279 
0280 int cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx,
0281                 u16 addr_low, u32 value, u32 mask)
0282 {
0283     struct cmdq_instruction inst = {};
0284     int err;
0285 
0286     inst.op = CMDQ_CODE_MASK;
0287     inst.mask = ~mask;
0288     err = cmdq_pkt_append_command(pkt, inst);
0289     if (err < 0)
0290         return err;
0291 
0292     inst.op = CMDQ_CODE_WRITE_S_MASK;
0293     inst.sop = high_addr_reg_idx;
0294     inst.offset = addr_low;
0295     inst.value = value;
0296 
0297     return cmdq_pkt_append_command(pkt, inst);
0298 }
0299 EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value);
0300 
0301 int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear)
0302 {
0303     struct cmdq_instruction inst = { {0} };
0304     u32 clear_option = clear ? CMDQ_WFE_UPDATE : 0;
0305 
0306     if (event >= CMDQ_MAX_EVENT)
0307         return -EINVAL;
0308 
0309     inst.op = CMDQ_CODE_WFE;
0310     inst.value = CMDQ_WFE_OPTION | clear_option;
0311     inst.event = event;
0312 
0313     return cmdq_pkt_append_command(pkt, inst);
0314 }
0315 EXPORT_SYMBOL(cmdq_pkt_wfe);
0316 
0317 int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
0318 {
0319     struct cmdq_instruction inst = { {0} };
0320 
0321     if (event >= CMDQ_MAX_EVENT)
0322         return -EINVAL;
0323 
0324     inst.op = CMDQ_CODE_WFE;
0325     inst.value = CMDQ_WFE_UPDATE;
0326     inst.event = event;
0327 
0328     return cmdq_pkt_append_command(pkt, inst);
0329 }
0330 EXPORT_SYMBOL(cmdq_pkt_clear_event);
0331 
0332 int cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event)
0333 {
0334     struct cmdq_instruction inst = {};
0335 
0336     if (event >= CMDQ_MAX_EVENT)
0337         return -EINVAL;
0338 
0339     inst.op = CMDQ_CODE_WFE;
0340     inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE;
0341     inst.event = event;
0342 
0343     return cmdq_pkt_append_command(pkt, inst);
0344 }
0345 EXPORT_SYMBOL(cmdq_pkt_set_event);
0346 
0347 int cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys,
0348           u16 offset, u32 value)
0349 {
0350     struct cmdq_instruction inst = { {0} };
0351     int err;
0352 
0353     inst.op = CMDQ_CODE_POLL;
0354     inst.value = value;
0355     inst.offset = offset;
0356     inst.subsys = subsys;
0357     err = cmdq_pkt_append_command(pkt, inst);
0358 
0359     return err;
0360 }
0361 EXPORT_SYMBOL(cmdq_pkt_poll);
0362 
0363 int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys,
0364                u16 offset, u32 value, u32 mask)
0365 {
0366     struct cmdq_instruction inst = { {0} };
0367     int err;
0368 
0369     inst.op = CMDQ_CODE_MASK;
0370     inst.mask = ~mask;
0371     err = cmdq_pkt_append_command(pkt, inst);
0372     if (err < 0)
0373         return err;
0374 
0375     offset = offset | CMDQ_POLL_ENABLE_MASK;
0376     err = cmdq_pkt_poll(pkt, subsys, offset, value);
0377 
0378     return err;
0379 }
0380 EXPORT_SYMBOL(cmdq_pkt_poll_mask);
0381 
0382 int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value)
0383 {
0384     struct cmdq_instruction inst = {};
0385 
0386     inst.op = CMDQ_CODE_LOGIC;
0387     inst.dst_t = CMDQ_REG_TYPE;
0388     inst.reg_dst = reg_idx;
0389     inst.value = value;
0390     return cmdq_pkt_append_command(pkt, inst);
0391 }
0392 EXPORT_SYMBOL(cmdq_pkt_assign);
0393 
0394 int cmdq_pkt_jump(struct cmdq_pkt *pkt, dma_addr_t addr)
0395 {
0396     struct cmdq_instruction inst = {};
0397 
0398     inst.op = CMDQ_CODE_JUMP;
0399     inst.offset = CMDQ_JUMP_RELATIVE;
0400     inst.value = addr >>
0401         cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan);
0402     return cmdq_pkt_append_command(pkt, inst);
0403 }
0404 EXPORT_SYMBOL(cmdq_pkt_jump);
0405 
0406 int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
0407 {
0408     struct cmdq_instruction inst = { {0} };
0409     int err;
0410 
0411     /* insert EOC and generate IRQ for each command iteration */
0412     inst.op = CMDQ_CODE_EOC;
0413     inst.value = CMDQ_EOC_IRQ_EN;
0414     err = cmdq_pkt_append_command(pkt, inst);
0415     if (err < 0)
0416         return err;
0417 
0418     /* JUMP to end */
0419     inst.op = CMDQ_CODE_JUMP;
0420     inst.value = CMDQ_JUMP_PASS >>
0421         cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan);
0422     err = cmdq_pkt_append_command(pkt, inst);
0423 
0424     return err;
0425 }
0426 EXPORT_SYMBOL(cmdq_pkt_finalize);
0427 
0428 int cmdq_pkt_flush_async(struct cmdq_pkt *pkt)
0429 {
0430     int err;
0431     struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
0432 
0433     err = mbox_send_message(client->chan, pkt);
0434     if (err < 0)
0435         return err;
0436     /* We can send next packet immediately, so just call txdone. */
0437     mbox_client_txdone(client->chan, 0);
0438 
0439     return 0;
0440 }
0441 EXPORT_SYMBOL(cmdq_pkt_flush_async);
0442 
0443 MODULE_LICENSE("GPL v2");