diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c index b8c06bd8659e91..6fc145aacaf029 100644 --- a/drivers/irqchip/irq-apple-aic.c +++ b/drivers/irqchip/irq-apple-aic.c @@ -226,7 +226,7 @@ static void aic_irq_eoi(struct irq_data *d) * Reading the interrupt reason automatically acknowledges and masks * the IRQ, so we just unmask it here if needed. */ - if (!irqd_irq_disabled(d) && !irqd_irq_masked(d)) + if (!irqd_irq_masked(d)) aic_irq_unmask(d); } diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index b4b780ea2ac8cc..4e5f3d67a33191 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -8,6 +8,11 @@ menuconfig MAILBOX if MAILBOX +config APPLE_MBOX + tristate "Apple RTKit Mailbox" + depends on ARM64 + default ARCH_APPLE + config ARM_MHU tristate "ARM MHU Mailbox" depends on ARM_AMBA diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index c2089f04887e99..bf946451264cf9 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -58,3 +58,7 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + +obj-$(CONFIG_APPLE_MBOX) += apple-asc-mailbox.o + +CFLAGS_apple-asc-mailbox.o := -I$(src) diff --git a/drivers/mailbox/apple-asc-mailbox-trace.h b/drivers/mailbox/apple-asc-mailbox-trace.h new file mode 100644 index 00000000000000..9578ea61840190 --- /dev/null +++ b/drivers/mailbox/apple-asc-mailbox-trace.h @@ -0,0 +1,81 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM apple_asc + +#if !defined(__APPLE_ASC_MAILBOX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __APPLE_ASC_MAILBOX_TRACE_H + +#include +#include + +#include "apple-asc-mailbox.h" + +DECLARE_EVENT_CLASS(apple_rtkit_log_msg, + TP_PROTO(struct apple_mbox *mbox, u64 msg0, u64 msg1), + TP_ARGS(mbox, msg0, msg1), + TP_STRUCT__entry( + __string(name, dev_name(mbox->dev)) + __field(u64, msg0) + __field(u64, msg1) + ), + TP_fast_assign( + __assign_str(name, dev_name(mbox->dev)); + __entry->msg0 = msg0; + __entry->msg1 = msg1; + ), + TP_printk("%s: %016llx %016llx (ep: 0x%02x)", + __get_str(name), + __entry->msg0, + __entry->msg1, + ((u8)__entry->msg1&0xff)) +); + +DEFINE_EVENT(apple_rtkit_log_msg, apple_mbox_hw_send, + TP_PROTO(struct apple_mbox *mbox, u64 msg0, u64 msg1), + TP_ARGS(mbox, msg0, msg1) +); + +DEFINE_EVENT(apple_rtkit_log_msg, apple_mbox_hw_recv, + TP_PROTO(struct apple_mbox *mbox, u64 msg0, u64 msg1), + TP_ARGS(mbox, msg0, msg1) +); + +DEFINE_EVENT(apple_rtkit_log_msg, apple_mbox_send_fifo_put, + TP_PROTO(struct apple_mbox *mbox, u64 msg0, u64 msg1), + TP_ARGS(mbox, msg0, msg1) +); + +DECLARE_EVENT_CLASS(apple_rtkit_irq_endis, + TP_PROTO(struct apple_mbox *mbox, bool enable), + TP_ARGS(mbox, enable), + TP_STRUCT__entry( + __string(name, dev_name(mbox->dev)) + __field(bool, enable) + ), + TP_fast_assign( + __assign_str(name, dev_name(mbox->dev)); + __entry->enable = enable; + ), + TP_printk("%s: %d", + __get_str(name), + __entry->enable) +); + +DEFINE_EVENT(apple_rtkit_irq_endis, apple_mbox_can_recv_irq_enable, + TP_PROTO(struct apple_mbox *mbox, bool enable), + TP_ARGS(mbox, enable) +); + +DEFINE_EVENT(apple_rtkit_irq_endis, apple_mbox_can_send_irq_enable, + TP_PROTO(struct apple_mbox *mbox, bool enable), + TP_ARGS(mbox, enable) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE apple-asc-mailbox-trace + +#include \ No newline at end of file diff --git a/drivers/mailbox/apple-asc-mailbox.c b/drivers/mailbox/apple-asc-mailbox.c new file mode 100644 index 00000000000000..171c46c2471d68 --- /dev/null +++ b/drivers/mailbox/apple-asc-mailbox.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "apple-asc-mailbox.h" + +#define CREATE_TRACE_POINTS +#include "apple-asc-mailbox-trace.h" + +static bool apple_mbox_hw_can_send(struct apple_mbox *apple_mbox) +{ + u32 mbox_ctrl = readl(apple_mbox->regs + APPLE_IOP_A2I_CONTROL); + return !(mbox_ctrl & APPLE_IOP_A2I_CONTROL_FULL); +} + +static void apple_mbox_hw_send(struct apple_mbox *apple_mbox, + struct apple_mbox_msg *msg) +{ + trace_apple_mbox_hw_send(apple_mbox, msg->msg, msg->info); + WARN_ON(!apple_mbox_hw_can_send(apple_mbox)); + writeq(msg->msg, apple_mbox->regs + APPLE_IOP_A2I_MBOX_DATA); + writeq(msg->info, apple_mbox->regs + APPLE_IOP_A2I_MBOX_INFO); +} + +static bool apple_mbox_hw_can_recv(struct apple_mbox *apple_mbox) +{ + u32 mbox_ctrl = readl(apple_mbox->regs + APPLE_IOP_I2A_CONTROL); + return !(mbox_ctrl & APPLE_IOP_I2A_CONTROL_EMPTY); +} + +static void apple_mbox_hw_recv(struct apple_mbox *apple_mbox, + struct apple_mbox_msg *msg) +{ + WARN_ON(!apple_mbox_hw_can_recv(apple_mbox)); + msg->msg = readq(apple_mbox->regs + APPLE_IOP_I2A_MBOX_DATA); + msg->info = readq(apple_mbox->regs + APPLE_IOP_I2A_MBOX_INFO); + trace_apple_mbox_hw_recv(apple_mbox, msg->msg, msg->info); +} + +static void apple_mbox_can_recv_irq_enable(struct apple_mbox *mbox, bool enable) +{ + trace_apple_mbox_can_recv_irq_enable(mbox, enable); + if (enable) + enable_irq(mbox->irq_can_recv); + else + disable_irq_nosync(mbox->irq_can_recv); +} + +static void apple_mbox_can_send_irq_enable(struct apple_mbox *mbox, bool enable) +{ + trace_apple_mbox_can_send_irq_enable(mbox, enable); + if (enable) + enable_irq(mbox->irq_can_send); + else + disable_irq_nosync(mbox->irq_can_send); +} + +static irqreturn_t apple_mbox_can_send_irq_handler(int irq, void *data) +{ + struct apple_mbox *apple_mbox = data; + struct apple_chan_priv *chan_priv; + bool keep_irq_enabled = false; + unsigned long flags; + int i; + + for (i = 0; i < APPLE_IOP_MAX_ACTIVE_CHANS; ++i) { + chan_priv = apple_mbox->chans[i].con_priv; + if (!chan_priv) + continue; + if (!chan_priv->needs_irq) + continue; + if (!apple_mbox_hw_can_send(apple_mbox)) + break; + + chan_priv->needs_irq = false; + mbox_chan_txdone(&apple_mbox->chans[i], 0); + } + + spin_lock_irqsave(&apple_mbox->lock, flags); + for (i = 0; i < APPLE_IOP_MAX_ACTIVE_CHANS; ++i) { + chan_priv = apple_mbox->chans[i].con_priv; + if (!chan_priv) + continue; + if (chan_priv->needs_irq) { + keep_irq_enabled = true; + break; + } + } + + if (keep_irq_enabled) { + apple_mbox->allow_can_send_irq_enable = false; + apple_mbox_can_send_irq_enable(apple_mbox, true); + } else { + apple_mbox->allow_can_send_irq_enable = true; + apple_mbox_can_send_irq_enable(apple_mbox, false); + } + spin_unlock_irqrestore(&apple_mbox->lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t apple_mbox_recv_irq_handler(int irq, void *data) +{ + struct apple_mbox *mbox = data; + struct apple_mbox_msg msg; + bool wake = false; + size_t len; + unsigned long flags; + + while (apple_mbox_hw_can_recv(mbox)) { + spin_lock_irqsave(&mbox->lock, flags); + if (unlikely(kfifo_avail(&mbox->recv_fifo) < 1)) { + apple_mbox_can_recv_irq_enable(mbox, false); + mbox->recv_full = true; + spin_unlock_irqrestore(&mbox->lock, flags); + return IRQ_WAKE_THREAD; + } + spin_unlock_irqrestore(&mbox->lock, flags); + + apple_mbox_hw_recv(mbox, &msg); + len = kfifo_in(&mbox->recv_fifo, &msg, 1); + WARN_ON(len != 1); + wake = true; + } + + if (wake) + return IRQ_WAKE_THREAD; + else + return IRQ_HANDLED; +} + +static irqreturn_t apple_mbox_recv_irq_thread(int irq, void *data) +{ + struct apple_mbox *apple_mbox = data; + struct mbox_controller *mbox = &apple_mbox->controller; + struct apple_mbox_msg msg; + struct apple_chan_priv *chan_priv; + int i; + u8 endpoint; + s8 chan_idx; + size_t len; + unsigned long flags; + + while (kfifo_len(&apple_mbox->recv_fifo) >= 1) { + len = kfifo_out(&apple_mbox->recv_fifo, &msg, 1); + WARN_ON(len != 1); + + endpoint = msg.info & 0xff; + chan_idx = apple_mbox->epmap[endpoint]; + if (chan_idx < 0) { + dev_err(mbox->dev, + "Received message for unknown endpoint #0x%02x.", + endpoint); + continue; + } + + chan_priv = mbox->chans[i].con_priv; + if (!chan_priv->enabled) { + dev_err(mbox->dev, + "Received message for disabled endpoint #0x%02x.", + endpoint); + continue; + } + + mbox_chan_received_data(&mbox->chans[chan_idx], + (void *)msg.msg); + } + + spin_lock_irqsave(&apple_mbox->lock, flags); + if (apple_mbox->recv_full) { + apple_mbox->recv_full = false; + apple_mbox_can_recv_irq_enable(apple_mbox, true); + } + spin_unlock_irqrestore(&apple_mbox->lock, flags); + + return IRQ_HANDLED; +} + +static struct mbox_chan *apple_mbox_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *spec) +{ + struct apple_mbox *apple_mbox = dev_get_drvdata(mbox->dev); + struct mbox_chan *chan; + struct apple_chan_priv *chan_priv; + int i, endpoint, free_chan_idx; + + if (spec->args_count != 1) + return ERR_PTR(-EINVAL); + + endpoint = spec->args[0]; + if (endpoint > APPLE_IOP_MAX_ENDPOINTS || endpoint < 0) + return ERR_PTR(-EINVAL); + + if (apple_mbox->epmap[endpoint] >= 0) + return ERR_PTR(-EBUSY); + + free_chan_idx = -1; + for (i = 0; i < mbox->num_chans; i++) { + chan_priv = mbox->chans[i].con_priv; + + if (!chan_priv) { + free_chan_idx = i; + break; + } + } + + if (free_chan_idx < 0) { + dev_err(mbox->dev, "No free channels left\n"); + return ERR_PTR(-EBUSY); + } + + chan = &mbox->chans[free_chan_idx]; + chan_priv = devm_kzalloc(mbox->dev, sizeof(*chan_priv), GFP_KERNEL); + if (!chan_priv) + return ERR_PTR(-ENOMEM); + + apple_mbox->epmap[endpoint] = free_chan_idx; + chan_priv->endpoint = endpoint; + chan_priv->apple_mbox = apple_mbox; + chan->con_priv = chan_priv; + return chan; +} + +static int apple_mbox_chan_send_data(struct mbox_chan *chan, void *data) +{ + struct apple_chan_priv *chan_priv = chan->con_priv; + struct apple_mbox *apple_mbox = chan_priv->apple_mbox; + struct apple_mbox_msg msg; + unsigned long flags; + int ret = 0; + + msg.info = chan_priv->endpoint; + msg.msg = (u64)data; + + spin_lock_irqsave(&apple_mbox->lock, flags); + + if (apple_mbox_hw_can_send(apple_mbox)) { + apple_mbox_hw_send(apple_mbox, &msg); + } else { + ret = -EBUSY; + if (apple_mbox->allow_can_send_irq_enable) { + apple_mbox->allow_can_send_irq_enable = false; + apple_mbox_can_send_irq_enable(apple_mbox, true); + } + chan_priv->needs_irq = true; + } + + spin_unlock_irqrestore(&apple_mbox->lock, flags); + return ret; +} + +static int apple_mbox_chan_startup(struct mbox_chan *chan) +{ + struct apple_chan_priv *chan_priv = chan->con_priv; + + chan_priv->enabled = true; + return 0; +} + +static void apple_mbox_chan_shutdown(struct mbox_chan *chan) +{ + struct apple_chan_priv *chan_priv = chan->con_priv; + + chan_priv->enabled = false; +} + +static const struct mbox_chan_ops apple_mbox_ops = { + .send_data = &apple_mbox_chan_send_data, + .startup = &apple_mbox_chan_startup, + .shutdown = &apple_mbox_chan_shutdown, +}; + +static int apple_mbox_probe(struct platform_device *pdev) +{ + int ret, i; + struct apple_mbox *mbox; + struct resource *regs; + struct device *dev = &pdev->dev; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + platform_set_drvdata(pdev, mbox); + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + mbox->dev = dev; + spin_lock_init(&mbox->lock); + INIT_KFIFO(mbox->recv_fifo); + + for (i = 0; i < APPLE_IOP_MAX_ENDPOINTS; ++i) + mbox->epmap[i] = -1; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -EINVAL; + + mbox->regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(mbox->regs)) + return PTR_ERR(mbox->regs); + + mbox->irq_can_send = platform_get_irq_byname(pdev, "can-send"); + if (mbox->irq_can_send < 0) + return -ENODEV; + mbox->irq_can_recv = platform_get_irq_byname(pdev, "can-recv"); + if (mbox->irq_can_recv < 0) + return -ENODEV; + + ret = devm_clk_bulk_get_all(dev, &mbox->clks); + if (ret) + return ret; + mbox->num_clks = ret; + + ret = clk_bulk_prepare_enable(mbox->num_clks, mbox->clks); + if (ret) + return ret; + + mbox->controller.dev = mbox->dev; + mbox->controller.num_chans = APPLE_IOP_MAX_ACTIVE_CHANS; + mbox->controller.chans = mbox->chans; + mbox->controller.ops = &apple_mbox_ops; + mbox->controller.of_xlate = &apple_mbox_of_xlate; + mbox->controller.txdone_irq = true; + mbox->controller.txdone_direct = true; + mbox->allow_can_send_irq_enable = true; + + ret = request_irq(mbox->irq_can_send, &apple_mbox_can_send_irq_handler, + IRQF_NO_AUTOEN, dev_name(dev), mbox); + if (ret) + goto err_clk_disable; + + ret = request_threaded_irq(mbox->irq_can_recv, + &apple_mbox_recv_irq_handler, + &apple_mbox_recv_irq_thread, 0, + dev_name(dev), mbox); + if (ret) + goto free_can_send_irq; + + ret = devm_mbox_controller_register(dev, &mbox->controller); + if (ret) + goto free_can_recv_irq; + + return ret; + +free_can_recv_irq: + free_irq(mbox->irq_can_recv, mbox); +free_can_send_irq: + free_irq(mbox->irq_can_send, mbox); +err_clk_disable: + clk_bulk_disable_unprepare(mbox->num_clks, mbox->clks); + return ret; +} + +/* + * these are probably all the same but use different compatible just in case + * we discover quirks later on + */ +static const struct of_device_id apple_mbox_of_match[] = { + { + .compatible = "apple,t8103-ans-mailbox", + }, + { + .compatible = "apple,t8103-smc-mailbox", + }, + { + .compatible = "apple,t8103-rtkit-mailbox", + }, + { + .compatible = "apple,t8103-sepos-mailbox", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_mbox_of_match); + +static int apple_mbox_remove(struct platform_device *pdev) +{ + struct apple_mbox *apple_mbox = platform_get_drvdata(pdev); + + free_irq(apple_mbox->irq_can_recv, apple_mbox); + free_irq(apple_mbox->irq_can_send, apple_mbox); + + return 0; +} + +static void apple_mbox_shutdown(struct platform_device *pdev) +{ + apple_mbox_remove(pdev); +} + +static struct platform_driver apple_mbox_driver = { + .driver = { + .name = "apple-mailbox", + .of_match_table = apple_mbox_of_match, + }, + .probe = apple_mbox_probe, + .remove = apple_mbox_remove, + .shutdown = apple_mbox_shutdown, +}; +module_platform_driver(apple_mbox_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("Apple mailbox driver"); diff --git a/drivers/mailbox/apple-asc-mailbox.h b/drivers/mailbox/apple-asc-mailbox.h new file mode 100644 index 00000000000000..ae5cb86668da73 --- /dev/null +++ b/drivers/mailbox/apple-asc-mailbox.h @@ -0,0 +1,78 @@ +#ifndef APPLE_ASC_MAILBOX +#define APPLE_ASC_MAILBOX 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* A2I = Application Processor (us) to I/O Processor (usually RTKit) */ +#define APPLE_IOP_A2I_CONTROL 0x110 +#define APPLE_IOP_A2I_CONTROL_FULL BIT(16) +#define APPLE_IOP_A2I_CONTROL_EMPTY BIT(17) + +#define APPLE_IOP_I2A_CONTROL 0x114 +#define APPLE_IOP_I2A_CONTROL_FULL BIT(16) +#define APPLE_IOP_I2A_CONTROL_EMPTY BIT(17) + +#define APPLE_IOP_A2I_MBOX_DATA 0x800 +#define APPLE_IOP_A2I_MBOX_INFO 0x808 +#define APPLE_IOP_I2A_MBOX_DATA 0x830 +#define APPLE_IOP_I2A_MBOX_INFO 0x838 + +/* max channels to save memory; IPC protocol supports up to 0x100 chans */ +#define APPLE_IOP_MAX_ACTIVE_CHANS 20 +#define APPLE_IOP_MAX_ENDPOINTS 0x100 + +struct apple_mbox; + +struct apple_chan_priv { + u8 endpoint; + bool needs_irq; + bool enabled; + struct apple_mbox *apple_mbox; +}; + +struct apple_mbox_msg { + u64 msg; + u64 info; +}; + +struct apple_mbox { + void __iomem *regs, *sart_regs; + struct resource *mmio_shmem; + int irq_can_send, irq_can_recv; + + bool allow_can_send_irq_enable; + + struct clk_bulk_data *clks; + int num_clks; + + spinlock_t lock; + + s8 epmap[APPLE_IOP_MAX_ENDPOINTS]; + struct mbox_chan chans[APPLE_IOP_MAX_ACTIVE_CHANS]; + + DECLARE_BITMAP(rtkit_endpoints, APPLE_IOP_MAX_ENDPOINTS); + + DECLARE_KFIFO(recv_fifo, struct apple_mbox_msg, 16); + bool recv_full; + + struct device *dev; + struct mbox_controller controller; +}; + +#endif \ No newline at end of file diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 3e7d4b20ab34fa..685914499bc368 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -23,6 +23,8 @@ static LIST_HEAD(mbox_cons); static DEFINE_MUTEX(con_mutex); +static void tx_tick(struct mbox_chan *chan, int r); + static int add_to_rbuf(struct mbox_chan *chan, void *mssg) { int idx; @@ -82,6 +84,12 @@ static void msg_submit(struct mbox_chan *chan) exit: spin_unlock_irqrestore(&chan->lock, flags); + /* check if the message has already been acked */ + if (!err && (chan->txdone_method & TXDONE_DIRECT)) { + tx_tick(chan, 0); + return; + } + /* kick start the timer immediately to avoid delays */ if (!err && (chan->txdone_method & TXDONE_BY_POLL)) { /* but only if not already active */ @@ -490,7 +498,15 @@ int mbox_controller_register(struct mbox_controller *mbox) else /* It has to be ACK then */ txdone = TXDONE_BY_ACK; - if (txdone == TXDONE_BY_POLL) { + if (mbox->txdone_direct) + txdone |= TXDONE_DIRECT; + + if ((txdone & TXDONE_DIRECT) && (txdone & TXDONE_BY_ACK)) { + dev_err(mbox->dev, "TXDONE_DIRECT and TXDONE_BY_ACK cannot be used together.\n"); + return -EINVAL; + } + + if (txdone & TXDONE_BY_POLL) { if (!mbox->ops->last_tx_done) { dev_err(mbox->dev, "last_tx_done method is absent\n"); diff --git a/drivers/mailbox/mailbox.h b/drivers/mailbox/mailbox.h index 046d6d258b320c..584e7f443429c4 100644 --- a/drivers/mailbox/mailbox.h +++ b/drivers/mailbox/mailbox.h @@ -6,5 +6,6 @@ #define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */ #define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */ #define TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */ +#define TXDONE_DIRECT BIT(3) /* ACK received when ops->send_data was succesful */ #endif /* __MAILBOX_H */ diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index 36d6ce673503c1..9900d968f66f3c 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -64,6 +64,11 @@ struct mbox_chan_ops { * @txdone_poll: If the controller can read but not report the TX * done. Ex, some register shows the TX status but * no interrupt rises. Ignored if 'txdone_irq' is set. + * @txdone_direct: Indicates that the API can assume that messages have + * been transmitted successfully when ops->submit_msg was + * successful. Must be used in conjunction with + * 'txdone_irq' or 'txdone_poll' when the message could + * not be submitted directly. * @txpoll_period: If 'txdone_poll' is in effect, the API polls for * last TX's status after these many millisecs * @of_xlate: Controller driver specific mapping of channel via DT @@ -78,6 +83,7 @@ struct mbox_controller { int num_chans; bool txdone_irq; bool txdone_poll; + bool txdone_direct; unsigned txpoll_period; struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox, const struct of_phandle_args *sp);