// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include /* * memory copy to tiny * @param dest: dest address * @param src: src address * @param size: memory size */ void mtk_memcpy_to_tinysys(void __iomem *dest, const void *src, uint32_t size) { int i; u32 __iomem *t = dest; const u32 *s = src; for (i = 0; i < ((size + 3) >> 2); i++) *t++ = *s++; } /* * memory copy from tiny * @param dest: dest address * @param src: src address * @param size: memory size */ void mtk_memcpy_from_tinysys(void *dest, const void __iomem *src, uint32_t size) { int i; u32 *t = dest; const u32 __iomem *s = src; for (i = 0; i < ((size + 3) >> 2); i++) *t++ = *s++; } /* * write data to mbox with ipi msg header * function must in critical context */ int mtk_mbox_write_hd(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int slot, void *msg) { unsigned int slot_ofs, size; struct mtk_mbox_info *minfo; struct mtk_ipi_msg *ipimsg; void __iomem *base; uint32_t len; unsigned long flags; if (!mbdev) { pr_notice("[MBOX]write header fail, dev null"); return MBOX_PLT_ERR; } if (mbox >= mbdev->count || !msg) { pr_notice("[MBOX]write header config err"); return MBOX_PARA_ERR; } minfo = &(mbdev->info_table[mbox]); base = minfo->base; slot_ofs = slot * MBOX_SLOT_SIZE; size = minfo->slot; ipimsg = (struct mtk_ipi_msg *)msg; len = ipimsg->ipihd.len; if ((slot_ofs + sizeof(struct mtk_ipi_msg_hd) + len) > size * MBOX_SLOT_SIZE) return MBOX_WRITE_SZ_ERR; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); /*ipi header and payload*/ if (mbdev->memcpy_to_tiny) { mbdev->memcpy_to_tiny((void __iomem *)(base + slot_ofs), ipimsg, sizeof(struct mtk_ipi_msg_hd)); mbdev->memcpy_to_tiny((void __iomem *) (base + slot_ofs + sizeof(struct mtk_ipi_msg_hd)), ipimsg->data, len); } else { mtk_memcpy_to_tinysys((void __iomem *)(base + slot_ofs), ipimsg, sizeof(struct mtk_ipi_msg_hd)); mtk_memcpy_to_tinysys((void __iomem *) (base + slot_ofs + sizeof(struct mtk_ipi_msg_hd)), ipimsg->data, len); } minfo->record.write_count++; spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_write_hd); /* * read data from mbox with ipi msg header * function must in critical context */ int mtk_mbox_read_hd(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int slot, void *dest) { unsigned int slot_ofs, size; struct mtk_mbox_info *minfo; struct mtk_ipi_msg_hd *ipihd; void __iomem *base; unsigned long flags; if (!mbdev) { pr_notice("[MBOX]read header fail, dev null"); return MBOX_PLT_ERR; } if (mbox >= mbdev->count || !dest) { pr_notice("[MBOX]read header config err"); return MBOX_PARA_ERR; } minfo = &(mbdev->info_table[mbox]); base = minfo->base; slot_ofs = slot * MBOX_SLOT_SIZE; size = minfo->slot; ipihd = (struct mtk_ipi_msg_hd *)(base + slot_ofs); if ((slot_ofs + sizeof(struct mtk_ipi_msg_hd) + ipihd->len) > size * MBOX_SLOT_SIZE) return MBOX_READ_SZ_ERR; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); /*ipi header and payload*/ if (mbdev->memcpy_from_tiny) mbdev->memcpy_from_tiny(dest, (void __iomem *) (base + slot_ofs + sizeof(struct mtk_ipi_msg_hd)), ipihd->len); else mtk_memcpy_from_tinysys(dest, (void __iomem *) (base + slot_ofs + sizeof(struct mtk_ipi_msg_hd)), ipihd->len); spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_read_hd); /* * write data to mbox, function must in critical context */ int mtk_mbox_write(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int slot, void *data, unsigned int len) { unsigned int slot_ofs, size; struct mtk_mbox_info *minfo; void __iomem *base; unsigned long flags; if (!mbdev) { pr_notice("[MBOX]write fail, dev or ptr null"); return MBOX_PLT_ERR; } if (mbox >= mbdev->count || !data) return MBOX_PARA_ERR; minfo = &(mbdev->info_table[mbox]); base = minfo->base; slot_ofs = slot * MBOX_SLOT_SIZE; size = minfo->slot; if (slot > size) return MBOX_WRITE_SZ_ERR; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); if (mbdev->memcpy_to_tiny) mbdev->memcpy_to_tiny((void __iomem *)(base + slot_ofs), data, len); else mtk_memcpy_to_tinysys((void __iomem *)(base + slot_ofs), data, len); minfo->record.write_count++; spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_write); /* * read data to user buffer, function must in critical context */ int mtk_mbox_read(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int slot, void *data, unsigned int len) { unsigned int slot_ofs, size; struct mtk_mbox_info *minfo; void __iomem *base; unsigned long flags; if (!mbdev || !data) { pr_notice("[MBOX]read fail,dev or ptr null"); return MBOX_PLT_ERR; } if (mbox >= mbdev->count) return MBOX_PARA_ERR; minfo = &(mbdev->info_table[mbox]); base = minfo->base; slot_ofs = slot * MBOX_SLOT_SIZE; size = minfo->slot; if (slot > size) return MBOX_READ_SZ_ERR; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); if (mbdev->memcpy_from_tiny) mbdev->memcpy_from_tiny(data, (void __iomem *)(base + slot_ofs), len); else mtk_memcpy_from_tinysys(data, (void __iomem *)(base + slot_ofs), len); spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_read); /* * clear mbox irq, * with read/write function must in critical context */ int mtk_mbox_clr_irq(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int irq) { struct mtk_mbox_info *minfo; if (!mbdev) return MBOX_PLT_ERR; if (mbox >= mbdev->count) return MBOX_IRQ_ERR; minfo = &(mbdev->info_table[mbox]); writel(irq, minfo->clr_irq_reg); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_clr_irq); /* * trigger mbox irq, * with read/write function must in critical context */ int mtk_mbox_trigger_irq(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int irq) { struct mtk_mbox_info *minfo; unsigned long flags; if (!mbdev) return MBOX_PLT_ERR; if (mbox >= mbdev->count) return MBOX_IRQ_ERR; minfo = &(mbdev->info_table[mbox]); spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); writel(irq, minfo->set_irq_reg); minfo->record.trig_irq_count++; spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_trigger_irq); /* * check mbox 32bits set irq reg status * with read/write function must in critical context * @return irq status 0: not triggered , other: irq triggered */ unsigned int mtk_mbox_check_send_irq(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int pin_index) { struct mtk_mbox_info *minfo; unsigned int reg, irq_state; unsigned long flags; if (!mbdev) return 0; if (mbox >= mbdev->count) return 0; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); irq_state = 0; minfo = &(mbdev->info_table[mbox]); if (minfo->send_status_reg) reg = readl(minfo->send_status_reg); else reg = readl(minfo->set_irq_reg); irq_state = (reg & (0x1 << pin_index)); if (irq_state) minfo->record.busy_count++; spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return irq_state; } EXPORT_SYMBOL_GPL(mtk_mbox_check_send_irq); /* * check mbox 32bits clr irq reg status * with read/write function must in critical context * @return irq status 0: not triggered , other: irq triggered */ unsigned int mtk_mbox_read_recv_irq(struct mtk_mbox_device *mbdev, unsigned int mbox) { struct mtk_mbox_info *minfo; unsigned int reg; if (!mbdev) return 0; if (mbox >= mbdev->count) return 0; minfo = &(mbdev->info_table[mbox]); if (minfo->recv_status_reg) reg = readl(minfo->recv_status_reg); else reg = readl(minfo->clr_irq_reg); return reg; } EXPORT_SYMBOL_GPL(mtk_mbox_read_recv_irq); /* * set mbox base address to init register * */ int mtk_mbox_set_base_reg(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int addr) { struct mtk_mbox_info *minfo; if (!mbdev) return MBOX_PLT_ERR; if (mbox >= mbdev->count) return MBOX_PARA_ERR; minfo = &(mbdev->info_table[mbox]); writel(addr, minfo->init_base_reg); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_set_base_reg); /* * set mbox base address, task context * */ int mtk_mbox_set_base_addr(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int addr) { struct mtk_mbox_info *minfo; unsigned long flags; int ret; if (!mbdev) return MBOX_PLT_ERR; if (mbox >= mbdev->count) return MBOX_PARA_ERR; spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); ret = mtk_mbox_set_base_reg(mbdev, mbox, addr); if (ret != MBOX_DONE) { spin_unlock_irqrestore( &mbdev->info_table[mbox].mbox_lock, flags); return ret; } minfo = &(mbdev->info_table[mbox]); writel(addr, minfo->base); spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_set_base_addr); /* * mtk_mbox_cb_register, register callback function * */ int mtk_mbox_cb_register(struct mtk_mbox_device *mbdev, unsigned int pin_offset, mbox_pin_cb_t mbox_pin_cb, void *prdata) { struct mtk_mbox_pin_recv *pin_recv; if (!mbdev) return MBOX_PLT_ERR; pin_recv = &(mbdev->pin_recv_table[pin_offset]); pin_recv->mbox_pin_cb = mbox_pin_cb; pin_recv->prdata = prdata; return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_cb_register); /* * mbox polling, context is protected by mbox_lock */ int mtk_mbox_polling(struct mtk_mbox_device *mbdev, unsigned int mbox, void *data, struct mtk_mbox_pin_recv *pin_recv) { struct mtk_mbox_info *minfo; unsigned long flags; unsigned int reg, irq_state; unsigned int recv_pin_index; int ret; if (!mbdev) return MBOX_PLT_ERR; if (mbox >= mbdev->count) return MBOX_PARA_ERR; recv_pin_index = pin_recv->pin_index; minfo = &(mbdev->info_table[mbox]); spin_lock_irqsave(&mbdev->info_table[mbox].mbox_lock, flags); /*check lock for */ if (pin_recv->lock == MBOX_PIN_BUSY) { spin_unlock_irqrestore( &mbdev->info_table[mbox].mbox_lock, flags); minfo->record.busy_count++; return MBOX_PIN_BUSY; } /*check bit*/ reg = mtk_mbox_read_recv_irq(mbdev, mbox); irq_state = (reg & (0x1 << recv_pin_index)); if (irq_state > 0) { /*clear bit*/ ret = mtk_mbox_clr_irq(mbdev, mbox, irq_state); } else { spin_unlock_irqrestore( &mbdev->info_table[mbox].mbox_lock, flags); minfo->record.busy_count++; return MBOX_PIN_BUSY; } spin_unlock_irqrestore(&mbdev->info_table[mbox].mbox_lock, flags); /*copy data*/ ret = mtk_mbox_read(mbdev, mbox, pin_recv->offset, data, pin_recv->msg_size * MBOX_SLOT_SIZE); if (ret != MBOX_DONE) return ret; pin_recv->recv_record.poll_count++; /*dump recv info*/ if (mbdev->log_enable) mtk_mbox_dump_recv_pin(mbdev, pin_recv); return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_polling); /* * set lock status */ static void mtk_mbox_set_lock(struct mtk_mbox_device *mbdev, unsigned int mbox, unsigned int lock) { struct mtk_mbox_pin_recv *pin_recv; int i; for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); if (pin_recv->mbox != mbox) continue; pin_recv->lock = lock; } } /* * mbox driver isr, in isr context */ static irqreturn_t mtk_mbox_isr(int irq, void *dev_id) { unsigned int mbox, irq_status, irq_temp; struct mtk_mbox_pin_recv *pin_recv; struct mtk_mbox_info *minfo = (struct mtk_mbox_info *)dev_id; struct mtk_mbox_device *mbdev = minfo->mbdev; struct mtk_ipi_msg_hd *ipihead; unsigned long flags; const uint64_t timeout_time = 50 * 1000 * 1000; /* 50ms */ uint64_t start_time, end_time, cbtimediff; uint32_t execute_count = 0; //void *user_data; int ret; int i; start_time = cpu_clock(0); mbox = minfo->id; ret = MBOX_DONE; spin_lock_irqsave(&minfo->mbox_lock, flags); /*lock pin*/ mtk_mbox_set_lock(mbdev, mbox, MBOX_PIN_BUSY); /*get irq status*/ irq_status = mtk_mbox_read_recv_irq(mbdev, mbox); irq_temp = 0; spin_unlock_irqrestore(&minfo->mbox_lock, flags); if (mbdev->pre_cb && mbdev->pre_cb(mbdev->prdata)) { ret = MBOX_PRE_CB_ERR; goto skip; } /*execute all receive pin handler*/ for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); if (pin_recv->mbox != mbox) continue; /*recv irq trigger*/ if (((0x1 << pin_recv->pin_index) & irq_status) > 0x0) { pin_recv->recv_record.recv_irq_count++; irq_temp = irq_temp | (0x1 << pin_recv->pin_index); /*check user buf*/ if (!pin_recv->pin_buf) { pr_err("[MBOX Error]null ptr dev=%s ipi_id=%d", mbdev->name, pin_recv->chan_id); BUG_ON(1); } if (minfo->opt == MBOX_OPT_QUEUE_DIR || minfo->opt == MBOX_OPT_QUEUE_SMEM) { /*queue mode*/ ipihead = (struct mtk_ipi_msg_hd *)(minfo->base + (pin_recv->offset * MBOX_SLOT_SIZE)); ret = mtk_mbox_read_hd(mbdev, mbox, pin_recv->offset, pin_recv->pin_buf); if (pin_recv->recv_opt == MBOX_RECV_MESSAGE && pin_recv->cb_ctx_opt == MBOX_CB_IN_ISR && pin_recv->mbox_pin_cb && ret == MBOX_DONE) { pin_recv->recv_record.pre_timestamp = cpu_clock(0); pin_recv->mbox_pin_cb(ipihead->id, pin_recv->prdata, pin_recv->pin_buf, (unsigned int)ipihead->len); pin_recv->recv_record.post_timestamp = cpu_clock(0); pin_recv->recv_record.cb_count++; execute_count++; cbtimediff = pin_recv->recv_record.post_timestamp - pin_recv->recv_record.pre_timestamp; if (cbtimediff > timeout_time) { pr_notice("[MBOX Error]dev=%s ipi_id=%d, timeout=%llu\n", mbdev->name, pin_recv->chan_id, cbtimediff); } } } else { /*direct mode*/ ret = mtk_mbox_read(mbdev, mbox, pin_recv->offset, pin_recv->pin_buf, pin_recv->msg_size * MBOX_SLOT_SIZE); if (pin_recv->recv_opt == MBOX_RECV_MESSAGE && pin_recv->cb_ctx_opt == MBOX_CB_IN_ISR && pin_recv->mbox_pin_cb && ret == MBOX_DONE) { pin_recv->recv_record.pre_timestamp = cpu_clock(0); pin_recv->mbox_pin_cb(pin_recv->chan_id, pin_recv->prdata, pin_recv->pin_buf, pin_recv->msg_size * MBOX_SLOT_SIZE); pin_recv->recv_record.post_timestamp = cpu_clock(0); pin_recv->recv_record.cb_count++; execute_count++; cbtimediff = pin_recv->recv_record.post_timestamp - pin_recv->recv_record.pre_timestamp; if (cbtimediff > timeout_time) { pr_notice("[MBOX Error]dev=%s ipi_id=%d, timeout=%llu\n", mbdev->name, pin_recv->chan_id, cbtimediff); } } } if (ret != MBOX_DONE) pr_err("[MBOX ISR]cp to buf fail,dev=%s chan=%d ret=%d", mbdev->name, pin_recv->chan_id, ret); /*dump recv info*/ if (mbdev->log_enable) mtk_mbox_dump_recv(mbdev, i); } } if (mbdev->post_cb && mbdev->post_cb(mbdev->prdata)) ret = MBOX_POST_CB_ERR; skip: if (ret == MBOX_PRE_CB_ERR) pr_notice("[MBOX ISR] pre_cb error, skip cb handle, dev=%s ret=%d", mbdev->name, ret); if (ret == MBOX_POST_CB_ERR) pr_notice("[MBOX ISR] post_cb error, dev=%s ret=%d", mbdev->name, ret); /*clear irq status*/ spin_lock_irqsave(&minfo->mbox_lock, flags); mtk_mbox_clr_irq(mbdev, mbox, irq_temp); /*release pin*/ mtk_mbox_set_lock(mbdev, mbox, MBOX_DONE); spin_unlock_irqrestore(&minfo->mbox_lock, flags); if (irq_temp == 0 && irq_status != 0) { pr_err("[MBOX ISR]dev=%s pin table err, status=%x", mbdev->name, irq_status); for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); mtk_mbox_dump_recv_pin(mbdev, pin_recv); } } /*notify all receive pin handler*/ for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); if (pin_recv->mbox != mbox) continue; /*recv irq trigger*/ if (((0x1 << pin_recv->pin_index) & irq_status) > 0x0) { /*notify task*/ if (mbdev->ipi_cb) { mbdev->ipi_cb(pin_recv, mbdev->ipi_priv); pin_recv->recv_record.notify_count++; } } } end_time = cpu_clock(0); if (end_time - start_time > timeout_time) { pr_notice("[MBOX Error]start=%llu, end=%llu diff=%llu, count=%u\n", start_time, end_time, end_time - start_time, execute_count); } return IRQ_HANDLED; } /* * mbox driver isr for scp only, in isr context */ static irqreturn_t mtk_mbox_scp_isr(int irq, void *dev_id) { unsigned int mbox, irq_status, irq_temp; struct mtk_mbox_pin_recv *pin_recv; struct mtk_mbox_info *minfo = (struct mtk_mbox_info *)dev_id; struct mtk_mbox_device *mbdev = minfo->mbdev; struct mtk_ipi_msg_hd *ipihead; unsigned long flags; const uint64_t timeout_time = 50 * 1000 * 1000; /* 50ms */ uint64_t start_time, end_time, cbtimediff; uint32_t execute_count = 0; //void *user_data; int ret; int i; start_time = cpu_clock(0); mbox = minfo->id; ret = MBOX_DONE; spin_lock_irqsave(&minfo->mbox_lock, flags); /*lock pin*/ mtk_mbox_set_lock(mbdev, mbox, MBOX_PIN_BUSY); /*get irq status*/ irq_status = mtk_mbox_read_recv_irq(mbdev, mbox); irq_temp = 0; spin_unlock_irqrestore(&minfo->mbox_lock, flags); if (mbdev->pre_cb && mbdev->pre_cb(mbdev->prdata)) { ret = MBOX_PRE_CB_ERR; goto skip; } /*execute all receive pin handler*/ for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); if (pin_recv->mbox != mbox) continue; /*recv irq trigger*/ if (((0x1 << pin_recv->pin_index) & irq_status) > 0x0) { pin_recv->recv_record.recv_irq_count++; irq_temp = irq_temp | (0x1 << pin_recv->pin_index); /*check user buf*/ if (!pin_recv->pin_buf) { pr_err("[MBOX Error]null ptr dev=%s ipi_id=%d", mbdev->name, pin_recv->chan_id); BUG_ON(1); } if (minfo->opt == MBOX_OPT_QUEUE_DIR || minfo->opt == MBOX_OPT_QUEUE_SMEM) { /*queue mode*/ ipihead = (struct mtk_ipi_msg_hd *)(minfo->base + (pin_recv->offset * MBOX_SLOT_SIZE)); ret = mtk_mbox_read_hd(mbdev, mbox, pin_recv->offset, pin_recv->pin_buf); if (pin_recv->recv_opt == MBOX_RECV_MESSAGE && pin_recv->cb_ctx_opt == MBOX_CB_IN_ISR && pin_recv->mbox_pin_cb && ret == MBOX_DONE) { pin_recv->recv_record.pre_timestamp = cpu_clock(0); pin_recv->mbox_pin_cb(ipihead->id, pin_recv->prdata, pin_recv->pin_buf, (unsigned int)ipihead->len); pin_recv->recv_record.post_timestamp = cpu_clock(0); pin_recv->recv_record.cb_count++; execute_count++; cbtimediff = pin_recv->recv_record.post_timestamp - pin_recv->recv_record.pre_timestamp; if (cbtimediff > timeout_time) { pr_notice("[MBOX Error]dev=%s ipi_id=%d, timeout=%llu\n", mbdev->name, pin_recv->chan_id, cbtimediff); } } } else { /*direct mode*/ ret = mtk_mbox_read(mbdev, mbox, pin_recv->offset, pin_recv->pin_buf, pin_recv->msg_size * MBOX_SLOT_SIZE); if (pin_recv->recv_opt == MBOX_RECV_MESSAGE && pin_recv->cb_ctx_opt == MBOX_CB_IN_ISR && pin_recv->mbox_pin_cb && ret == MBOX_DONE) { pin_recv->recv_record.pre_timestamp = cpu_clock(0); pin_recv->mbox_pin_cb(pin_recv->chan_id, pin_recv->prdata, pin_recv->pin_buf, pin_recv->msg_size * MBOX_SLOT_SIZE); pin_recv->recv_record.post_timestamp = cpu_clock(0); pin_recv->recv_record.cb_count++; execute_count++; cbtimediff = pin_recv->recv_record.post_timestamp - pin_recv->recv_record.pre_timestamp; if (cbtimediff > timeout_time) { pr_notice("[MBOX Error]dev=%s ipi_id=%d, timeout=%llu\n", mbdev->name, pin_recv->chan_id, cbtimediff); } } } if (ret != MBOX_DONE) pr_err("[MBOX ISR]cp to buf fail,dev=%s chan=%d ret=%d", mbdev->name, pin_recv->chan_id, ret); /*dump recv info*/ if (mbdev->log_enable) mtk_mbox_dump_recv(mbdev, i); } } if (mbdev->post_cb && mbdev->post_cb(mbdev->prdata)) ret = MBOX_POST_CB_ERR; skip: if (ret == MBOX_PRE_CB_ERR) pr_notice("[MBOX ISR] pre_cb error, skip cb handle, dev=%s ret=%d", mbdev->name, ret); if (ret == MBOX_POST_CB_ERR) pr_notice("[MBOX ISR] post_cb error, dev=%s ret=%d", mbdev->name, ret); /*clear irq status*/ spin_lock_irqsave(&minfo->mbox_lock, flags); mtk_mbox_clr_irq(mbdev, mbox, irq_temp); /*release pin*/ mtk_mbox_set_lock(mbdev, mbox, MBOX_DONE); spin_unlock_irqrestore(&minfo->mbox_lock, flags); if (irq_temp == 0 && irq_status != 0) { pr_err("[MBOX ISR]dev=%s pin table err, status=%x", mbdev->name, irq_status); for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); mtk_mbox_dump_recv_pin(mbdev, pin_recv); } } /*notify all receive pin handler*/ for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); if (pin_recv->mbox != mbox) continue; /*recv irq trigger*/ if (((0x1 << pin_recv->pin_index) & irq_status) > 0x0) { /*notify task*/ if (mbdev->ipi_cb) { mbdev->ipi_cb(pin_recv, mbdev->ipi_priv); pin_recv->recv_record.notify_count++; } } } end_time = cpu_clock(0); if (end_time - start_time > timeout_time) { pr_notice("[MBOX Error]start=%llu, end=%llu diff=%llu, count=%u\n", start_time, end_time, end_time - start_time, execute_count); } return IRQ_HANDLED; } /* * mtk_smem_init, initial share memory * */ int mtk_smem_init(struct platform_device *pdev, struct mtk_mbox_device *mbdev, unsigned int mbox, void __iomem *base, void __iomem *set_irq_reg, void __iomem *clr_irq_reg, void __iomem *send_status_reg, void __iomem *recv_status_reg) { struct mtk_mbox_info *minfo; char name[32]; int ret; minfo = &(mbdev->info_table[mbox]); minfo->base = base; minfo->set_irq_reg = set_irq_reg; minfo->clr_irq_reg = clr_irq_reg; minfo->send_status_reg = send_status_reg; minfo->recv_status_reg = recv_status_reg; minfo->enable = true; minfo->id = mbox; minfo->mbdev = mbdev; minfo->is64d = 0; spin_lock_init(&minfo->mbox_lock); snprintf(name, sizeof(name), "mbox%d", mbox); minfo->irq_num = platform_get_irq_byname(pdev, name); if (minfo->irq_num < 0) { pr_err("MBOX %d can't find IRQ\n", mbox); goto smem_fail; } ret = request_irq(minfo->irq_num, mtk_mbox_isr, IRQF_TRIGGER_NONE, "MBOX_ISR", (void *) minfo); if (ret) { pr_err("MBOX %d request irq Failed\n", mbox); goto smem_fail; } return MBOX_DONE; smem_fail: return MBOX_CONFIG_ERR; } EXPORT_SYMBOL_GPL(mtk_smem_init); /* * mtk_mbox_probe , porbe and initial mbox * */ int mtk_mbox_probe(struct platform_device *pdev, struct mtk_mbox_device *mbdev, unsigned int mbox) { struct mtk_mbox_info *minfo; char name[32]; int ret; struct device *dev = &pdev->dev; struct resource *res; minfo = &(mbdev->info_table[mbox]); if (pdev) { snprintf(name, sizeof(name), "mbox%d_base", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->base = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->base)) pr_err("MBOX %d can't remap base\n", mbox); minfo->slot = (unsigned int)resource_size(res)/MBOX_SLOT_SIZE; /*init reg*/ snprintf(name, sizeof(name), "mbox%d_init", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->init_base_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->init_base_reg)) pr_err("MBOX %d can't find init reg\n", mbox); /*set irq reg*/ snprintf(name, sizeof(name), "mbox%d_set", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->set_irq_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->set_irq_reg)) { pr_err("MBOX %d can't find set reg\n", mbox); goto mtk_mbox_probe_fail; } /*clear reg*/ snprintf(name, sizeof(name), "mbox%d_clr", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->clr_irq_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->clr_irq_reg)) { pr_err("MBOX %d can't find clr reg\n", mbox); goto mtk_mbox_probe_fail; } /*send status reg*/ snprintf(name, sizeof(name), "mbox%d_send", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->send_status_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->send_status_reg)) { pr_notice("MBOX %d can't find send status reg\n", mbox); minfo->send_status_reg = NULL; } /*recv status reg*/ snprintf(name, sizeof(name), "mbox%d_recv", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->recv_status_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->recv_status_reg)) { pr_notice("MBOX %d can't find recv status reg\n", mbox); minfo->recv_status_reg = NULL; } snprintf(name, sizeof(name), "mbox%d", mbox); minfo->irq_num = platform_get_irq_byname(pdev, name); if (minfo->irq_num < 0) { pr_err("MBOX %d can't find IRQ\n", mbox); goto mtk_mbox_probe_fail; } minfo->enable = true; minfo->id = mbox; minfo->mbdev = mbdev; spin_lock_init(&minfo->mbox_lock); ret = request_irq(minfo->irq_num, mtk_mbox_isr, IRQF_TRIGGER_NONE, "MBOX_ISR", (void *) minfo); if (ret) { pr_err("MBOX %d request irq Failed\n", mbox); goto mtk_mbox_probe_fail; } } return MBOX_DONE; mtk_mbox_probe_fail: return MBOX_CONFIG_ERR; } EXPORT_SYMBOL_GPL(mtk_mbox_probe); /* * mtk_mbox_scp_probe , porbe and initial mbox for scp only * */ int mtk_mbox_scp_probe(struct platform_device *pdev, struct mtk_mbox_device *mbdev, unsigned int mbox) { struct mtk_mbox_info *minfo; char name[32]; int ret; struct device *dev = &pdev->dev; struct resource *res; minfo = &(mbdev->info_table[mbox]); if (pdev) { snprintf(name, sizeof(name), "mbox%d_base", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->base = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->base)) pr_err("MBOX %d can't remap base\n", mbox); minfo->slot = (unsigned int)resource_size(res)/MBOX_SLOT_SIZE; /*init reg*/ snprintf(name, sizeof(name), "mbox%d_init", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->init_base_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->init_base_reg)) pr_err("MBOX %d can't find init reg\n", mbox); /*set irq reg*/ snprintf(name, sizeof(name), "mbox%d_set", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->set_irq_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->set_irq_reg)) { pr_err("MBOX %d can't find set reg\n", mbox); goto mtk_mbox_probe_fail; } /*clear reg*/ snprintf(name, sizeof(name), "mbox%d_clr", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->clr_irq_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->clr_irq_reg)) { pr_err("MBOX %d can't find clr reg\n", mbox); goto mtk_mbox_probe_fail; } /*send status reg*/ snprintf(name, sizeof(name), "mbox%d_send", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->send_status_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->send_status_reg)) { pr_notice("MBOX %d can't find send status reg\n", mbox); minfo->send_status_reg = NULL; } /*recv status reg*/ snprintf(name, sizeof(name), "mbox%d_recv", mbox); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); minfo->recv_status_reg = devm_ioremap_resource(dev, res); if (IS_ERR((void const *) minfo->recv_status_reg)) { pr_notice("MBOX %d can't find recv status reg\n", mbox); minfo->recv_status_reg = NULL; } snprintf(name, sizeof(name), "mbox%d", mbox); minfo->irq_num = platform_get_irq_byname(pdev, name); if (minfo->irq_num < 0) { pr_err("MBOX %d can't find IRQ\n", mbox); goto mtk_mbox_probe_fail; } minfo->enable = true; minfo->id = mbox; minfo->mbdev = mbdev; spin_lock_init(&minfo->mbox_lock); ret = request_threaded_irq(minfo->irq_num, NULL, mtk_mbox_scp_isr, IRQF_ONESHOT, "MBOX_SCP_ISR", (void *) minfo); if (ret) { pr_err("MBOX %d request irq Failed\n", mbox); goto mtk_mbox_probe_fail; } } return MBOX_DONE; mtk_mbox_probe_fail: return MBOX_CONFIG_ERR; } EXPORT_SYMBOL_GPL(mtk_mbox_scp_probe); /* *mbox print receive pin function */ void mtk_mbox_print_recv(struct mtk_mbox_device *mbdev, struct mtk_mbox_pin_recv *pin_recv) { pr_notice("[MBOX]dev=%s recv mbox=%u off=%u cv_opt=%u ctx_opt=%u mg_sz=%u p_idx=%u id=%u\n" , mbdev->name , pin_recv->mbox , pin_recv->offset , pin_recv->recv_opt , pin_recv->cb_ctx_opt , pin_recv->msg_size , pin_recv->pin_index , pin_recv->chan_id); pr_notice("[MBOX]dev=%s recv id=%u poll=%u cv_irq=%u noti=%u cb=%u pre=%lld po=%lld\n" , mbdev->name , pin_recv->chan_id , pin_recv->recv_record.poll_count , pin_recv->recv_record.recv_irq_count , pin_recv->recv_record.notify_count , pin_recv->recv_record.cb_count , pin_recv->recv_record.pre_timestamp , pin_recv->recv_record.post_timestamp); } EXPORT_SYMBOL_GPL(mtk_mbox_print_recv); /* *mbox print send pin function */ void mtk_mbox_print_send(struct mtk_mbox_device *mbdev, struct mtk_mbox_pin_send *pin_send) { pr_notice("[MBOX]dev=%s send mbox=%u off=%u s_opt=%u mg_sz=%u p_idx=%u id=%u\n" , mbdev->name , pin_send->mbox , pin_send->offset , pin_send->send_opt , pin_send->msg_size , pin_send->pin_index , pin_send->chan_id); } EXPORT_SYMBOL_GPL(mtk_mbox_print_send); /* *mbox print mbox function */ void mtk_mbox_print_minfo(struct mtk_mbox_device *mbdev, struct mtk_mbox_info *minfo) { pr_notice("[MBOX]dev=%s mbox id=%u slot=%u opt=%u base=%p set_reg=%p clr_reg=%p init_reg=%p s_sta=%p cv_sta=%p\n" , mbdev->name , minfo->id , minfo->slot , minfo->opt , minfo->base , minfo->set_irq_reg , minfo->clr_irq_reg , minfo->init_base_reg , minfo->send_status_reg , minfo->recv_status_reg); pr_notice("[MBOX]dev=%s write=%u busy=%u tri_irq=%u\n" , mbdev->name , minfo->record.write_count , minfo->record.busy_count , minfo->record.trig_irq_count); } EXPORT_SYMBOL_GPL(mtk_mbox_print_minfo); /* *mbox information dump */ void mtk_mbox_dump_all(struct mtk_mbox_device *mbdev) { struct mtk_mbox_pin_recv *pin_recv; struct mtk_mbox_pin_send *pin_send; struct mtk_mbox_info *minfo; int i; if (!mbdev) return; pr_notice("[MBOX]dev=%s recv count=%u send count=%u\n", mbdev->name, mbdev->recv_count, mbdev->send_count); for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); mtk_mbox_print_recv(mbdev, pin_recv); } for (i = 0; i < mbdev->send_count; i++) { pin_send = &(mbdev->pin_send_table[i]); mtk_mbox_print_send(mbdev, pin_send); } for (i = 0; i < mbdev->count; i++) { minfo = &(mbdev->info_table[i]); mtk_mbox_print_minfo(mbdev, minfo); } } EXPORT_SYMBOL_GPL(mtk_mbox_dump_all); /* *mbox single receive pin information dump */ void mtk_mbox_dump_recv(struct mtk_mbox_device *mbdev, unsigned int pin) { struct mtk_mbox_pin_recv *pin_recv; if (mbdev) { if (pin < mbdev->recv_count) { pin_recv = &(mbdev->pin_recv_table[pin]); mtk_mbox_print_recv(mbdev, pin_recv); } } } EXPORT_SYMBOL_GPL(mtk_mbox_dump_recv); /* *mbox single receive pin information dump */ void mtk_mbox_dump_recv_pin(struct mtk_mbox_device *mbdev, struct mtk_mbox_pin_recv *pin_recv) { unsigned int irq_reg; if (mbdev && pin_recv) { irq_reg = mtk_mbox_read_recv_irq(mbdev, pin_recv->mbox); pr_err("[MBOX]dev=%s mbox=%u recv irq status=%x\n", mbdev->name, pin_recv->mbox, irq_reg); mtk_mbox_print_recv(mbdev, pin_recv); } } EXPORT_SYMBOL_GPL(mtk_mbox_dump_recv_pin); /* *mbox single send pin information dump */ void mtk_mbox_dump_send(struct mtk_mbox_device *mbdev, unsigned int pin) { struct mtk_mbox_pin_send *pin_send; if (mbdev) { if (pin < mbdev->send_count) { pin_send = &(mbdev->pin_send_table[pin]); mtk_mbox_print_send(mbdev, pin_send); } } } EXPORT_SYMBOL_GPL(mtk_mbox_dump_send); /* *mbox single mbox information dump */ void mtk_mbox_dump(struct mtk_mbox_device *mbdev, unsigned int mbox) { struct mtk_mbox_info *minfo; if (mbdev) { if (mbox < mbdev->count) { minfo = &(mbdev->info_table[mbox]); mtk_mbox_print_minfo(mbdev, minfo); } } } EXPORT_SYMBOL_GPL(mtk_mbox_dump); /* *mbox log enable function */ int mtk_mbox_log_enable(struct mtk_mbox_device *mbdev, bool enable) { if (!mbdev) return MBOX_PLT_ERR; mbdev->log_enable = enable; return MBOX_DONE; } EXPORT_SYMBOL_GPL(mtk_mbox_log_enable); /* *mbox reset record */ void mtk_mbox_reset_record(struct mtk_mbox_device *mbdev) { struct mtk_mbox_pin_recv *pin_recv; struct mtk_mbox_info *minfo; int i; if (!mbdev) return; for (i = 0; i < mbdev->recv_count; i++) { pin_recv = &(mbdev->pin_recv_table[i]); pin_recv->recv_record.poll_count = 0; pin_recv->recv_record.recv_irq_count = 0; pin_recv->recv_record.notify_count = 0; pin_recv->recv_record.cb_count = 0; pin_recv->recv_record.pre_timestamp = 0; pin_recv->recv_record.post_timestamp = 0; } for (i = 0; i < mbdev->count; i++) { minfo = &(mbdev->info_table[i]); minfo->record.write_count = 0; minfo->record.busy_count = 0; minfo->record.trig_irq_count = 0; } } EXPORT_SYMBOL_GPL(mtk_mbox_reset_record); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MediaTek Tinysys Mbox driver");