| /* |
| * Samsung Exynos SoC series VIPx driver |
| * |
| * Copyright (c) 2018 Samsung Electronics Co., Ltd |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/delay.h> |
| |
| #include "vipx-log.h" |
| #include "vipx-io.h" |
| #include "vipx-mailbox.h" |
| |
| static inline int __vipx_mailbox_is_empty(struct vipx_mailbox_head *head) |
| { |
| vipx_check(); |
| return head->wmsg_idx == head->rmsg_idx; |
| } |
| |
| static void __vipx_mailbox_dump(struct vipx_mailbox_head *head, void *data) |
| { |
| int idx; |
| |
| vipx_enter(); |
| vipx_info("mailbox haed (%u,%u,%u,%u)\n", |
| head->rmsg_idx, head->wmsg_idx, |
| head->mbox_size, head->elem_size); |
| |
| for (idx = 0; idx < head->mbox_size; ++idx) { |
| vipx_info("mailbox data (%d/%u)\n", idx, head->mbox_size - 1); |
| print_hex_dump(KERN_INFO, "[VIPx]:", DUMP_PREFIX_OFFSET, 32, 4, |
| data + (idx * head->elem_size), |
| head->elem_size, false); |
| } |
| vipx_leave(); |
| } |
| |
| void vipx_mailbox_dump(struct vipx_mailbox_ctrl *mctrl) |
| { |
| vipx_enter(); |
| vipx_info("h2d normal mailbox dump\n"); |
| __vipx_mailbox_dump(&mctrl->h2d_normal_head, |
| mctrl->h2d_normal_data); |
| |
| vipx_info("h2d urgent mailbox dump\n"); |
| __vipx_mailbox_dump(&mctrl->h2d_urgent_head, |
| &mctrl->h2d_urgent_data); |
| |
| vipx_info("d2h normal mailbox dump\n"); |
| __vipx_mailbox_dump(&mctrl->d2h_normal_head, |
| &mctrl->d2h_normal_data); |
| |
| vipx_info("d2h urgent mailbox dump\n"); |
| __vipx_mailbox_dump(&mctrl->d2h_urgent_head, |
| &mctrl->d2h_urgent_data); |
| vipx_leave(); |
| } |
| |
| static ssize_t __vipx_mailbox_get_remain_size(struct vipx_mailbox_head *head) |
| { |
| unsigned int rmsg_idx, wmsg_idx, mbox_size; |
| unsigned int ridx, widx; |
| unsigned int used; |
| |
| vipx_check(); |
| rmsg_idx = head->rmsg_idx; |
| wmsg_idx = head->wmsg_idx; |
| mbox_size = head->mbox_size; |
| |
| ridx = (rmsg_idx >= mbox_size) ? rmsg_idx - mbox_size : rmsg_idx; |
| widx = (wmsg_idx >= mbox_size) ? wmsg_idx - mbox_size : wmsg_idx; |
| |
| if (widx > ridx) |
| used = widx - ridx; |
| else if (widx < ridx) |
| used = (mbox_size - ridx) + widx; |
| else if (wmsg_idx != rmsg_idx) |
| used = mbox_size; |
| else |
| used = 0; |
| |
| return mbox_size - used; |
| } |
| |
| int vipx_mailbox_check_full(struct vipx_mailbox_ctrl *mctrl, unsigned int type, |
| int wait) |
| { |
| int ret; |
| struct vipx_mailbox_head *head; |
| int try_count = 1000; |
| ssize_t remain_size; |
| |
| vipx_enter(); |
| if (type == NORMAL_MSG) { |
| head = &mctrl->h2d_normal_head; |
| } else if (type == URGENT_MSG) { |
| head = &mctrl->h2d_urgent_head; |
| } else { |
| ret = -EINVAL; |
| vipx_err("invalid mbox type(%u)\n", type); |
| goto p_err; |
| } |
| |
| while (try_count) { |
| remain_size = __vipx_mailbox_get_remain_size(head); |
| if (remain_size > 0) { |
| break; |
| } else if (remain_size < 0) { |
| ret = -EFAULT; |
| vipx_err("index of mbox(%u) is invalid (%u/%u/%u/%u)\n", |
| type, head->wmsg_idx, head->rmsg_idx, |
| head->mbox_size, head->elem_size); |
| goto p_err; |
| } else if (wait) { |
| vipx_warn("mbox(%u) is full (%u/%u/%u/%u/%d)\n", |
| type, head->wmsg_idx, head->rmsg_idx, |
| head->mbox_size, head->elem_size, |
| try_count); |
| udelay(10); |
| try_count--; |
| } else { |
| break; |
| } |
| } |
| |
| if (!remain_size) { |
| ret = -EBUSY; |
| vipx_err("mbox(%u) is full (%u/%u/%u/%u)\n", |
| type, head->wmsg_idx, head->rmsg_idx, |
| head->mbox_size, head->elem_size); |
| goto p_err; |
| } |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| int vipx_mailbox_write(struct vipx_mailbox_ctrl *mctrl, unsigned int type, |
| void *msg, size_t size) |
| { |
| int ret; |
| struct vipx_mailbox_head *head; |
| unsigned int wmsg_idx, mbox_size; |
| unsigned int widx; |
| struct vipx_h2d_message *h2d_msg; |
| void *wptr; |
| struct vipx_h2d_message *debug; |
| |
| vipx_enter(); |
| if (size > sizeof(struct vipx_h2d_message)) { |
| ret = -EINVAL; |
| vipx_err("message size(%zu/%zu) for mbox(%u) is invalid\n", |
| size, sizeof(struct vipx_h2d_message), type); |
| goto p_err; |
| } |
| |
| if (type == NORMAL_MSG) { |
| head = &mctrl->h2d_normal_head; |
| h2d_msg = mctrl->h2d_normal_data; |
| } else if (type == URGENT_MSG) { |
| head = &mctrl->h2d_urgent_head; |
| h2d_msg = mctrl->h2d_urgent_data; |
| } else { |
| ret = -EINVAL; |
| vipx_err("invalid mbox type(%u)\n", type); |
| goto p_err; |
| } |
| |
| wmsg_idx = head->wmsg_idx; |
| mbox_size = head->mbox_size; |
| |
| widx = (wmsg_idx >= mbox_size) ? wmsg_idx - mbox_size : wmsg_idx; |
| |
| wptr = &h2d_msg[widx]; |
| vipx_io_copy_mem2io(wptr, msg, size); |
| |
| widx = wmsg_idx + 1; |
| |
| debug = msg; |
| vipx_dbg("[MBOX(%d)-W] mbox[v%u/r:%u/w:%u/s:%u/e:%u]\n", |
| type, mctrl->mbox_version, |
| head->rmsg_idx, head->wmsg_idx, |
| mbox_size, head->elem_size); |
| vipx_dbg("[MBOX(%d)-W] message[v%u/t:%u/s:%u/tid:%u/mid:%u]\n", |
| type, debug->head.msg_version, debug->head.msg_type, |
| debug->head.msg_size, debug->head.trans_id, |
| debug->head.msg_id); |
| #ifdef DEBUG_LOG_MAILBOX_DUMP |
| for (ret = 0; ret < debug->head.msg_size >> 2; ++ret) { |
| vipx_dbg("[MBOX(%d)-W][%4d] %#10x\n", |
| type, ret, ((int *)&debug->body)[ret]); |
| } |
| #endif |
| |
| head->wmsg_idx = (widx >= (mbox_size << 1)) ? |
| widx - (mbox_size << 1) : widx; |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| int vipx_mailbox_check_reply(struct vipx_mailbox_ctrl *mctrl, unsigned int type, |
| int wait) |
| { |
| int ret; |
| struct vipx_mailbox_head *head; |
| int try_count = 1000; |
| |
| vipx_enter(); |
| if (type == NORMAL_MSG) { |
| head = &mctrl->d2h_normal_head; |
| } else if (type == URGENT_MSG) { |
| head = &mctrl->d2h_urgent_head; |
| } else { |
| ret = -EINVAL; |
| vipx_err("invalid mbox type (%u)\n", type); |
| goto p_err; |
| } |
| |
| while (try_count) { |
| ret = __vipx_mailbox_is_empty(head); |
| if (ret) { |
| if (!wait) |
| break; |
| |
| udelay(10); |
| try_count--; |
| } else { |
| break; |
| } |
| } |
| |
| if (ret) { |
| ret = -EFAULT; |
| goto p_err; |
| } |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| int vipx_mailbox_read(struct vipx_mailbox_ctrl *mctrl, unsigned int type, |
| void *msg) |
| { |
| int ret; |
| struct vipx_mailbox_head *head; |
| unsigned int rmsg_idx, mbox_size; |
| unsigned int ridx; |
| struct vipx_d2h_message *d2h_msg; |
| void *rptr; |
| struct vipx_d2h_message *debug; |
| |
| vipx_enter(); |
| if (type == NORMAL_MSG) { |
| head = &mctrl->d2h_normal_head; |
| d2h_msg = mctrl->d2h_normal_data; |
| } else if (type == URGENT_MSG) { |
| head = &mctrl->d2h_urgent_head; |
| d2h_msg = mctrl->d2h_urgent_data; |
| } else { |
| ret = -EINVAL; |
| vipx_err("invalid mbox type(%u)\n", type); |
| goto p_err; |
| } |
| |
| rmsg_idx = head->rmsg_idx; |
| mbox_size = head->mbox_size; |
| |
| ridx = (rmsg_idx >= mbox_size) ? rmsg_idx - mbox_size : rmsg_idx; |
| |
| rptr = &d2h_msg[ridx]; |
| vipx_io_copy_io2mem(msg, rptr, sizeof(struct vipx_d2h_message)); |
| |
| ridx = rmsg_idx + 1; |
| |
| debug = msg; |
| vipx_dbg("[MBOX(%d)-R] mbox[v%u/r:%u/w:%u/s:%u/e:%u]\n", |
| type, mctrl->mbox_version, |
| head->rmsg_idx, head->wmsg_idx, |
| mbox_size, head->elem_size); |
| vipx_dbg("[MBOX(%d)-R] message[v%u/t:%u/s:%u/tid:%u/mid:%u]\n", |
| type, debug->head.msg_version, debug->head.msg_type, |
| debug->head.msg_size, debug->head.trans_id, |
| debug->head.msg_id); |
| #ifdef DEBUG_LOG_MAILBOX_DUMP |
| for (ret = 0; ret < debug->head.msg_size >> 2; ++ret) { |
| vipx_dbg("[MBOX(%d)-R][%4d] %#10x\n", |
| type, ret, ((int *)&debug->body)[ret]); |
| } |
| #endif |
| |
| head->rmsg_idx = (ridx >= (mbox_size << 1)) ? |
| ridx - (mbox_size << 1) : ridx; |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| static void __vipx_mailbox_init(struct vipx_mailbox_head *head, |
| unsigned short mbox_size, unsigned short elem_size) |
| { |
| vipx_enter(); |
| head->rmsg_idx = 0; |
| head->wmsg_idx = 0; |
| |
| head->mbox_size = mbox_size; |
| head->elem_size = elem_size; |
| vipx_leave(); |
| } |
| |
| int vipx_mailbox_init(struct vipx_mailbox_ctrl *mctrl) |
| { |
| vipx_enter(); |
| mctrl->mbox_version = MAILBOX_VERSION; |
| mctrl->msg_version = MESSAGE_VERSION; |
| |
| __vipx_mailbox_init(&mctrl->h2d_normal_head, MAX_NORMAL_MSG_COUNT, |
| sizeof(mctrl->h2d_normal_data[0])); |
| __vipx_mailbox_init(&mctrl->h2d_urgent_head, MAX_URGENT_MSG_COUNT, |
| sizeof(mctrl->h2d_urgent_data[0])); |
| __vipx_mailbox_init(&mctrl->d2h_normal_head, MAX_NORMAL_MSG_COUNT, |
| sizeof(mctrl->d2h_normal_data[0])); |
| __vipx_mailbox_init(&mctrl->d2h_urgent_head, MAX_URGENT_MSG_COUNT, |
| sizeof(mctrl->d2h_urgent_data[0])); |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_mailbox_deinit(struct vipx_mailbox_ctrl *mctrl) |
| { |
| vipx_enter(); |
| vipx_leave(); |
| return 0; |
| } |