From 52bf44f451720abee21467aadb95faa303e937a2 Mon Sep 17 00:00:00 2001 From: Shunzhou Jiang Date: Tue, 22 Oct 2019 15:54:40 +0800 Subject: [PATCH] mbox: sm1: add devnode and supoort duplex [1/1] PD#SWPL-15621 Problem: add new feature Solution: add devnode and support duplex Verify: test pass on sm1 skt Change-Id: I0990a08d611d039eb33e5f2a4ac4e8b86453c367 Signed-off-by: Shunzhou Jiang --- MAINTAINERS | 4 + drivers/amlogic/firmware/bl40_module.c | 80 ++++++++++++++-- drivers/amlogic/firmware/bl40_module.h | 29 ++++++ drivers/amlogic/mailbox/meson_mhu.c | 119 +++++++++++++++++++++--- drivers/amlogic/mailbox/meson_mhu.h | 1 + drivers/amlogic/mailbox/scpi_protocol.c | 41 ++++++-- include/linux/amlogic/scpi_protocol.h | 8 ++ 7 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 drivers/amlogic/firmware/bl40_module.h diff --git a/MAINTAINERS b/MAINTAINERS index ab288b00c83a..f48ad1eed56e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15255,3 +15255,7 @@ AMLOGIC UVM DRIVER M: Ao.Xu F: drivers/amlogic/uvm/meson_uvm.c F: drivers/amlogic/uvm/meson_uvm.h + +AMLOGIC SM1/G12A BL40 HEADER +M: shunzhou jiang +F: drivers/amlogic/firmware/bl40_module.h diff --git a/drivers/amlogic/firmware/bl40_module.c b/drivers/amlogic/firmware/bl40_module.c index e7436a63ac95..ef1866ca0414 100644 --- a/drivers/amlogic/firmware/bl40_module.c +++ b/drivers/amlogic/firmware/bl40_module.c @@ -43,15 +43,51 @@ #include #include #include +#include +#include +#include "bl40_module.h" #define AMLOGIC_BL40_BOOTUP 0x8200004E struct bl40_info { char name[30]; }; +struct bl40_msg { + struct list_head list; + struct bl40_msg_buf msg_buf; + struct completion complete; +}; + +/*for listen list*/ +spinlock_t lock; struct device *device; +static LIST_HEAD(bl40_list); #define BL40_IOC_MAGIC 'H' -#define BL40_FIRMWARE_LOAD _IOWR(BL40_IOC_MAGIC, 1, struct bl40_info) +#define BL40_FIRMWARE_LOAD _IOWR(BL40_IOC_MAGIC, 1, struct bl40_info) +#define BL40_CMD_SEND _IOWR(BL40_IOC_MAGIC, 2, struct bl40_msg_buf) +#define BL40_CMD_LISTEN _IOWR(BL40_IOC_MAGIC, 3, struct bl40_msg_buf) + +void bl40_rx_msg(void *msg, int size) +{ + struct list_head *list; + struct bl40_msg *bl40_msg; + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + if (list_empty(&bl40_list)) { + spin_unlock_irqrestore(&lock, flags); + dev_err(device, "List is NULL\n"); + return; + } + list_for_each(list, &bl40_list) { + bl40_msg = list_entry(list, struct bl40_msg, list); + bl40_msg->msg_buf.size = size; + memcpy(bl40_msg->msg_buf.buf, msg, size); + complete(&bl40_msg->complete); + break; + } + spin_unlock_irqrestore(&lock, flags); +} static long bl40_miscdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) @@ -59,14 +95,15 @@ static long bl40_miscdev_ioctl(struct file *fp, unsigned int cmd, int ret = 0; const struct firmware *firmware; void __user *argp = (void __user *)arg; - struct bl40_info bl40_info; - unsigned long phy_addr; - void *virt_addr = NULL; - struct arm_smccc_res res = {0}; - size_t size; switch (cmd) { - case BL40_FIRMWARE_LOAD: + case BL40_FIRMWARE_LOAD: { + struct bl40_info bl40_info; + unsigned long phy_addr; + void *virt_addr = NULL; + struct arm_smccc_res res = {0}; + size_t size; + ret = copy_from_user((void *)&bl40_info, argp, sizeof(bl40_info)); if (ret < 0) @@ -96,12 +133,39 @@ static long bl40_miscdev_ioctl(struct file *fp, unsigned int cmd, pr_info("free memory\n"); devm_kfree(device, virt_addr); ret = res.a0; + } + break; + case BL40_CMD_SEND: { + struct bl40_msg_buf bl40_buf; + + ret = copy_from_user((void *)&bl40_buf, + argp, sizeof(bl40_buf)); + pr_debug("Enter BL40_CMD_SEND\n"); + scpi_send_bl40(SCPI_CMD_BL4_SEND, &bl40_buf); + ret = copy_to_user(argp, &bl40_buf, sizeof(bl40_buf)); + } + break; + case BL40_CMD_LISTEN: { + struct bl40_msg bl40_msg; + unsigned long flags; + + init_completion(&bl40_msg.complete); + spin_lock_irqsave(&lock, flags); + list_add_tail(&bl40_msg.list, &bl40_list); + spin_unlock_irqrestore(&lock, flags); + wait_for_completion(&bl40_msg.complete); + ret = copy_to_user(argp, &bl40_msg.msg_buf, + sizeof(struct bl40_msg_buf)); + spin_lock_irqsave(&lock, flags); + list_del(&bl40_msg.list); + spin_unlock_irqrestore(&lock, flags); + } break; default: pr_info("Not have this cmd\n"); break; }; - pr_info("bl40 ioctl\n"); + pr_debug("bl40 ioctl\n"); return ret; } diff --git a/drivers/amlogic/firmware/bl40_module.h b/drivers/amlogic/firmware/bl40_module.h new file mode 100644 index 000000000000..f240f3245703 --- /dev/null +++ b/drivers/amlogic/firmware/bl40_module.h @@ -0,0 +1,29 @@ +/* + * drivers/amlogic/firmware/bl40_module.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __BL40_MODULE_H__ +#define __BL40_MODULE_H__ + +#ifdef CONFIG_AMLOGIC_FIRMWARE +void bl40_rx_msg(void *msg, int size); +#else +static inline void bl40_rx_msg(void *msg, int size) +{ +} +#endif + +#endif /*__BL40_MODULE_H__*/ diff --git a/drivers/amlogic/mailbox/meson_mhu.c b/drivers/amlogic/mailbox/meson_mhu.c index f4e363cd934d..88e82638bec0 100644 --- a/drivers/amlogic/mailbox/meson_mhu.c +++ b/drivers/amlogic/mailbox/meson_mhu.c @@ -31,15 +31,19 @@ #include #include #include +#include #include "meson_mhu.h" +#include "../firmware/bl40_module.h" struct device *the_scpi_device; u32 num_scp_chans; - +u32 send_listen_chans; +u32 isr_send; +u32 isr_m4; #define DRIVER_NAME "meson_mhu" - +#define MHU_BUFFER_SIZE 0x100 /* * +--------------------+-------+---------------+ * | Hardware Register | Offset| Driver View | @@ -99,9 +103,17 @@ struct mhu_ctlr { void __iomem *mbox_base; void __iomem *payload_base; struct mbox_controller mbox_con; - struct mhu_chan channels[CHANNEL_MAX]; + struct mhu_chan *channels; }; +void bl40_rx_callback(struct mbox_client *cl, void *msg) +{ + struct mhu_data_buf *data = (struct mhu_data_buf *)msg; + + pr_debug("call %s\n", __func__); + bl40_rx_msg(data->rx_buf, data->rx_size); +} + static irqreturn_t mbox_handler(int irq, void *p) { struct mbox_chan *link = (struct mbox_chan *)p; @@ -111,18 +123,42 @@ static irqreturn_t mbox_handler(int irq, void *p) void __iomem *payload = ctlr->payload_base; int idx = chan->index; struct mhu_data_buf *data; - u32 status = readl(mbox_base + RX_STATUS(idx)); + u32 status = 0; + u32 is_send_isr = BIT(idx) & isr_send; + u32 is_send_chan = BIT(idx) & send_listen_chans; + + if (isr_m4) { + if (BIT(idx) & isr_m4) + idx = 1; + else + idx = 0; + } + + if (is_send_isr) + status = 1; + else + status = readl(mbox_base + RX_STATUS(idx)); + pr_debug("isr %d idx %x sts %x\n", irq, idx, status); if (status && irq == chan->rx_irq) { data = chan->data; - if (!data) + if (!data) { + pr_err("data is null\n"); return IRQ_NONE; /* spurious */ - if (data->rx_buf) - memcpy(data->rx_buf, payload + RX_PAYLOAD(idx), - data->rx_size); - chan->data = NULL; + } + if (data->rx_buf) { + if (is_send_isr) { + memcpy(data->rx_buf, payload + TX_PAYLOAD(idx), + data->rx_size); + } else + memcpy(data->rx_buf, payload + RX_PAYLOAD(idx), + data->rx_size); + } mbox_chan_received_data(link, data); - writel(~0, mbox_base + RX_CLEAR(idx)); + if (!is_send_isr) + writel(~0, mbox_base + RX_CLEAR(idx)); + if (is_send_chan) + chan->data = NULL; } return IRQ_HANDLED; @@ -141,6 +177,12 @@ static int mhu_send_data(struct mbox_chan *link, void *msg) return -EINVAL; chan->data = data; + if (isr_m4) { + if (BIT(idx) & isr_m4) + idx = 1; + else + idx = 0; + } if (data->tx_buf) memcpy(payload + TX_PAYLOAD(idx), data->tx_buf, data->tx_size); writel(data->cmd, mbox_base + TX_SET(idx)); @@ -190,7 +232,10 @@ static int mhu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mbox_chan *l; struct resource *res; - int idx; + struct mbox_client *cl; + int idx, err; + u32 mbox_chans = 0; + int bit_chans = 0; static const char * const channel_names[] = { CHANNEL_LOW_PRIORITY, CHANNEL_HIGH_PRIORITY @@ -227,18 +272,30 @@ static int mhu_probe(struct platform_device *pdev) of_property_read_u32(dev->of_node, "num-chans-to-scp", &num_scp_chans); if (num_scp_chans == 0 || num_scp_chans > 2) num_scp_chans = CHANNEL_MAX; - - l = devm_kzalloc(dev, sizeof(*l) * CHANNEL_MAX, GFP_KERNEL); + send_listen_chans = 0; + of_property_read_u32(dev->of_node, "send-isr-bits", &send_listen_chans); + of_property_read_u32(dev->of_node, "ack-isr-bits", &isr_send); + of_property_read_u32(dev->of_node, "m4-isr-bits", &isr_m4); + + of_property_read_u32(dev->of_node, "mbox-chans", &mbox_chans); + if (!mbox_chans) + mbox_chans = CHANNEL_MAX; + l = devm_kzalloc(dev, sizeof(*l) * mbox_chans, GFP_KERNEL); if (!l) return -ENOMEM; + ctlr->channels = devm_kzalloc(dev, + sizeof(struct mhu_chan) * mbox_chans, + GFP_KERNEL); + if (!ctlr->channels) + return -ENOMEM; ctlr->mbox_con.chans = l; - ctlr->mbox_con.num_chans = CHANNEL_MAX; + ctlr->mbox_con.num_chans = mbox_chans; ctlr->mbox_con.txdone_irq = true; ctlr->mbox_con.ops = &mhu_ops; ctlr->mbox_con.dev = dev; - for (idx = 0; idx < CHANNEL_MAX; idx++) { + for (idx = 0; idx < mbox_chans; idx++) { chan = &ctlr->channels[idx]; chan->index = idx; chan->ctlr = ctlr; @@ -250,6 +307,7 @@ static int mhu_probe(struct platform_device *pdev) return -ENXIO; } l[idx].con_priv = chan; + bit_chans |= BIT(idx); } if (mbox_controller_register(&ctlr->mbox_con)) { @@ -258,6 +316,37 @@ static int mhu_probe(struct platform_device *pdev) } the_scpi_device = dev; + + if (!send_listen_chans) + goto probe_done; + + for (idx = 0; idx < mbox_chans; idx++) { + if (BIT(idx) & send_listen_chans) + continue; + cl = devm_kzalloc(dev, sizeof(struct mbox_client), + GFP_KERNEL); + cl->dev = dev; + cl->rx_callback = bl40_rx_callback; + l[idx].cl = cl; + chan = &ctlr->channels[idx]; + chan->data = devm_kzalloc(dev, + sizeof(struct mhu_data_buf), + GFP_KERNEL); + chan->data->rx_buf = devm_kzalloc(dev, + MHU_BUFFER_SIZE, + GFP_KERNEL); + if (!chan->data->rx_buf) + return -ENOMEM; + chan->data->rx_size = MHU_BUFFER_SIZE; + err = request_threaded_irq(chan->rx_irq, mbox_handler, + NULL, IRQF_ONESHOT, DRIVER_NAME, + &l[idx]); + if (err) { + dev_err(dev, "request irq error\n"); + return err; + } + } +probe_done: return 0; } diff --git a/drivers/amlogic/mailbox/meson_mhu.h b/drivers/amlogic/mailbox/meson_mhu.h index 48f4df471560..7bb51c7a7171 100644 --- a/drivers/amlogic/mailbox/meson_mhu.h +++ b/drivers/amlogic/mailbox/meson_mhu.h @@ -32,3 +32,4 @@ struct mhu_data_buf { extern struct device *the_scpi_device; extern u32 num_scp_chans; +extern u32 send_listen_chans; diff --git a/drivers/amlogic/mailbox/scpi_protocol.c b/drivers/amlogic/mailbox/scpi_protocol.c index f2185e5c789b..e4813165bdc6 100644 --- a/drivers/amlogic/mailbox/scpi_protocol.c +++ b/drivers/amlogic/mailbox/scpi_protocol.c @@ -123,7 +123,10 @@ static int high_priority_cmds[] = { SCPI_CMD_INIT_DSP, }; -static int m4_cmds[] = {-1}; +static int bl4_cmds[] = { + SCPI_CMD_BL4_SEND, + SCPI_CMD_BL4_LISTEN, +}; static struct scpi_dvfs_info *scpi_opps[MAX_DVFS_DOMAINS]; @@ -149,8 +152,8 @@ static bool high_priority_chan_supported(int cmd) if (cmd == high_priority_cmds[idx]) return true; } else { - for (idx = 0; idx < ARRAY_SIZE(m4_cmds); idx++) - if (cmd == m4_cmds[idx]) + for (idx = 0; idx < ARRAY_SIZE(bl4_cmds); idx++) + if (cmd == bl4_cmds[idx]) return true; } return false; @@ -170,11 +173,22 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) struct mbox_client cl = {0}; struct mhu_data_buf *data = scpi_buf->data; u32 status; + int chan_idx; cl.dev = the_scpi_device; cl.rx_callback = scpi_rx_callback; - chan = mbox_request_channel(&cl, high_priority); + /* use to find send mbox index */ + if (send_listen_chans) { + chan_idx = 31 - __builtin_clz(send_listen_chans); + if (!high_priority) + chan_idx = 31 - __builtin_clz( + send_listen_chans ^ BIT(chan_idx)); + } else { + chan_idx = high_priority; + } + + chan = mbox_request_channel(&cl, chan_idx); if (IS_ERR(chan)) return PTR_ERR(chan); @@ -228,8 +242,12 @@ static int scpi_execute_cmd(struct scpi_data_buf *scpi_buf) return -EINVAL; data = scpi_buf->data; high_priority = high_priority_chan_supported(data->cmd); - data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id, - data->tx_size); + if (!high_priority || (num_scp_chans == CHANNEL_MAX)) { + data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id, + data->tx_size); + } else if (high_priority && (num_scp_chans != CHANNEL_MAX)) { + data->cmd = data->tx_size; + } data->cl_data = scpi_buf; return send_scpi_cmd(scpi_buf, high_priority); @@ -697,3 +715,14 @@ int scpi_unlock_bl40(void) return 0; } EXPORT_SYMBOL_GPL(scpi_unlock_bl40); + +int scpi_send_bl40(unsigned int cmd, struct bl40_msg_buf *bl40_buf) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + + SCPI_SETUP_DBUF_SIZE(sdata, mdata, SCPI_CL_NONE, + cmd, bl40_buf->buf, bl40_buf->size, + bl40_buf, sizeof(struct bl40_msg_buf)); + return scpi_execute_cmd(&sdata); +} diff --git a/include/linux/amlogic/scpi_protocol.h b/include/linux/amlogic/scpi_protocol.h index b433f5942b9c..7c284d7fb901 100644 --- a/include/linux/amlogic/scpi_protocol.h +++ b/include/linux/amlogic/scpi_protocol.h @@ -74,6 +74,8 @@ enum scpi_std_cmd { SCPI_CMD_GET_CEC1 = 0xB4, SCPI_CMD_GET_CEC2 = 0xB5, SCPI_CMD_BL4_WAIT_UNLOCK = 0xD6, + SCPI_CMD_BL4_SEND = 0xD7, + SCPI_CMD_BL4_LISTEN = 0xD8, SCPI_CMD_COUNT }; @@ -96,6 +98,11 @@ struct scpi_dvfs_info { struct scpi_opp_entry *opp; } __packed; +struct bl40_msg_buf { + int size; + char buf[256]; +} __packed; + unsigned long scpi_clk_get_val(u16 clk_id); int scpi_clk_set_val(u16 clk_id, unsigned long rate); int scpi_dvfs_get_idx(u8 domain); @@ -114,4 +121,5 @@ u8 scpi_get_ethernet_calc(void); int scpi_get_cpuinfo(enum scpi_get_pfm_type type, u32 *freq, u32 *vol); int scpi_init_dsp_cfg0(u32 id, u32 addr, u32 cfg0); int scpi_unlock_bl40(void); +int scpi_send_bl40(unsigned int cmd, struct bl40_msg_buf *bl40_buf); #endif /*_SCPI_PROTOCOL_H_*/ -- 2.20.1