// SPDX-License-Identifier: GPL-2.0 /* Marvell Octeon EP (EndPoint) Ethernet Driver * * Copyright (C) 2020 Marvell. * */ #include #include #include #include #include #include #include #include #include #include #include "octep_ctrl_mbox.h" #include "octep_config.h" #include "octep_main.h" /* Timeout in msecs for message response */ #define OCTEP_CTRL_MBOX_MSG_TIMEOUT_MS 100 /* Time in msecs to wait for message response */ #define OCTEP_CTRL_MBOX_MSG_WAIT_MS 10 /* Size of mbox info in bytes */ #define OCTEP_CTRL_MBOX_INFO_SZ 256 /* Size of mbox host to fw queue info in bytes */ #define OCTEP_CTRL_MBOX_H2FQ_INFO_SZ 16 /* Size of mbox fw to host queue info in bytes */ #define OCTEP_CTRL_MBOX_F2HQ_INFO_SZ 16 #define OCTEP_CTRL_MBOX_TOTAL_INFO_SZ (OCTEP_CTRL_MBOX_INFO_SZ + \ OCTEP_CTRL_MBOX_H2FQ_INFO_SZ + \ OCTEP_CTRL_MBOX_F2HQ_INFO_SZ) #define OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(m) (m) #define OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(m) ((m) + 8) #define OCTEP_CTRL_MBOX_INFO_HOST_VERSION(m) ((m) + 16) #define OCTEP_CTRL_MBOX_INFO_HOST_STATUS(m) ((m) + 24) #define OCTEP_CTRL_MBOX_INFO_FW_VERSION(m) ((m) + 136) #define OCTEP_CTRL_MBOX_INFO_FW_STATUS(m) ((m) + 144) #define OCTEP_CTRL_MBOX_H2FQ_INFO(m) ((m) + OCTEP_CTRL_MBOX_INFO_SZ) #define OCTEP_CTRL_MBOX_H2FQ_PROD(m) (OCTEP_CTRL_MBOX_H2FQ_INFO(m)) #define OCTEP_CTRL_MBOX_H2FQ_CONS(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 4) #define OCTEP_CTRL_MBOX_H2FQ_SZ(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 8) #define OCTEP_CTRL_MBOX_F2HQ_INFO(m) ((m) + \ OCTEP_CTRL_MBOX_INFO_SZ + \ OCTEP_CTRL_MBOX_H2FQ_INFO_SZ) #define OCTEP_CTRL_MBOX_F2HQ_PROD(m) (OCTEP_CTRL_MBOX_F2HQ_INFO(m)) #define OCTEP_CTRL_MBOX_F2HQ_CONS(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 4) #define OCTEP_CTRL_MBOX_F2HQ_SZ(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 8) static const u32 mbox_hdr_sz = sizeof(union octep_ctrl_mbox_msg_hdr); static u32 octep_ctrl_mbox_circq_inc(u32 index, u32 inc, u32 sz) { return (index + inc) % sz; } static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 sz) { return sz - (abs(pi - ci) % sz); } static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 sz) { return (abs(pi - ci) % sz); } int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox) { u64 magic_num, status, fw_versions; if (!mbox) return -EINVAL; if (!mbox->barmem) { pr_info("octep_ctrl_mbox : Invalid barmem %p\n", mbox->barmem); return -EINVAL; } magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(mbox->barmem)); if (magic_num != OCTEP_CTRL_MBOX_MAGIC_NUMBER) { pr_info("octep_ctrl_mbox : Invalid magic number %llx\n", magic_num); return -EINVAL; } status = readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)); if (status != OCTEP_CTRL_MBOX_STATUS_READY) { pr_info("octep_ctrl_mbox : Firmware is not ready.\n"); return -EINVAL; } fw_versions = readq(OCTEP_CTRL_MBOX_INFO_FW_VERSION(mbox->barmem)); mbox->min_fw_version = ((fw_versions & 0xffffffff00000000ull) >> 32); mbox->max_fw_version = (fw_versions & 0xffffffff); mbox->barmem_sz = readl(OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(mbox->barmem)); writeq(OCTEP_CTRL_MBOX_STATUS_INIT, OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem)); mutex_init(&mbox->h2fq_lock); mutex_init(&mbox->f2hq_lock); mbox->h2fq.sz = readl(OCTEP_CTRL_MBOX_H2FQ_SZ(mbox->barmem)); mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD(mbox->barmem); mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS(mbox->barmem); mbox->h2fq.hw_q = mbox->barmem + OCTEP_CTRL_MBOX_TOTAL_INFO_SZ; mbox->f2hq.sz = readl(OCTEP_CTRL_MBOX_F2HQ_SZ(mbox->barmem)); mbox->f2hq.hw_prod = OCTEP_CTRL_MBOX_F2HQ_PROD(mbox->barmem); mbox->f2hq.hw_cons = OCTEP_CTRL_MBOX_F2HQ_CONS(mbox->barmem); mbox->f2hq.hw_q = mbox->barmem + OCTEP_CTRL_MBOX_TOTAL_INFO_SZ + mbox->h2fq.sz; writeq(mbox->version, OCTEP_CTRL_MBOX_INFO_HOST_VERSION(mbox->barmem)); /* ensure ready state is seen after everything is initialized */ wmb(); writeq(OCTEP_CTRL_MBOX_STATUS_READY, OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem)); pr_info("Octep ctrl mbox : Init successful.\n"); return 0; } static void octep_write_mbox_data(struct octep_ctrl_mbox_q *q, u32 *pi, u32 ci, void *buf, u32 w_sz) { u8 __iomem *qbuf; u32 cp_sz; /* Assumption: Caller has ensured enough write space */ qbuf = (q->hw_q + *pi); if (*pi < ci) { /* copy entire w_sz */ memcpy_toio(qbuf, buf, w_sz); *pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz); } else { /* copy up to end of queue */ cp_sz = min((q->sz - *pi), w_sz); memcpy_toio(qbuf, buf, cp_sz); w_sz -= cp_sz; *pi = octep_ctrl_mbox_circq_inc(*pi, cp_sz, q->sz); if (w_sz) { /* roll over and copy remaining w_sz */ buf += cp_sz; qbuf = (q->hw_q + *pi); memcpy_toio(qbuf, buf, w_sz); *pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz); } } } int octep_ctrl_mbox_send(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg) { struct octep_ctrl_mbox_msg_buf *sg; struct octep_ctrl_mbox_q *q; u32 pi, ci, buf_sz, w_sz; int s; if (!mbox || !msg) return -EINVAL; if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY) return -EIO; mutex_lock(&mbox->h2fq_lock); q = &mbox->h2fq; pi = readl(q->hw_prod); ci = readl(q->hw_cons); if (octep_ctrl_mbox_circq_space(pi, ci, q->sz) < (msg->hdr.s.sz + mbox_hdr_sz)) { mutex_unlock(&mbox->h2fq_lock); return -EAGAIN; } octep_write_mbox_data(q, &pi, ci, (void *)&msg->hdr, mbox_hdr_sz); buf_sz = msg->hdr.s.sz; for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) { sg = &msg->sg_list[s]; w_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz; octep_write_mbox_data(q, &pi, ci, sg->msg, w_sz); buf_sz -= w_sz; } writel(pi, q->hw_prod); mutex_unlock(&mbox->h2fq_lock); return 0; } static void octep_read_mbox_data(struct octep_ctrl_mbox_q *q, u32 pi, u32 *ci, void *buf, u32 r_sz) { u8 __iomem *qbuf; u32 cp_sz; /* Assumption: Caller has ensured enough read space */ qbuf = (q->hw_q + *ci); if (*ci < pi) { /* copy entire r_sz */ memcpy_fromio(buf, qbuf, r_sz); *ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz); } else { /* copy up to end of queue */ cp_sz = min((q->sz - *ci), r_sz); memcpy_fromio(buf, qbuf, cp_sz); r_sz -= cp_sz; *ci = octep_ctrl_mbox_circq_inc(*ci, cp_sz, q->sz); if (r_sz) { /* roll over and copy remaining r_sz */ buf += cp_sz; qbuf = (q->hw_q + *ci); memcpy_fromio(buf, qbuf, r_sz); *ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz); } } } int octep_ctrl_mbox_recv(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg) { struct octep_ctrl_mbox_msg_buf *sg; u32 pi, ci, r_sz, buf_sz, q_depth; struct octep_ctrl_mbox_q *q; int s; if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY) return -EIO; mutex_lock(&mbox->f2hq_lock); q = &mbox->f2hq; pi = readl(q->hw_prod); ci = readl(q->hw_cons); q_depth = octep_ctrl_mbox_circq_depth(pi, ci, q->sz); if (q_depth < mbox_hdr_sz) { mutex_unlock(&mbox->f2hq_lock); return -EAGAIN; } octep_read_mbox_data(q, pi, &ci, (void *)&msg->hdr, mbox_hdr_sz); buf_sz = msg->hdr.s.sz; for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) { sg = &msg->sg_list[s]; r_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz; octep_read_mbox_data(q, pi, &ci, sg->msg, r_sz); buf_sz -= r_sz; } writel(ci, q->hw_cons); mutex_unlock(&mbox->f2hq_lock); return 0; } int octep_ctrl_mbox_uninit(struct octep_ctrl_mbox *mbox) { if (!mbox) return -EINVAL; if (!mbox->barmem) return -EINVAL; writeq(0, OCTEP_CTRL_MBOX_INFO_HOST_VERSION(mbox->barmem)); writeq(OCTEP_CTRL_MBOX_STATUS_INVALID, OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem)); /* ensure uninit state is written before uninitialization */ wmb(); mutex_destroy(&mbox->h2fq_lock); mutex_destroy(&mbox->f2hq_lock); pr_info("Octep ctrl mbox : Uninit successful.\n"); return 0; }