0001
0002
0003
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
0159
0160
0161
0162
0163
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
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
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
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");