438 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/*3 * Apple mailbox driver4 *5 * Copyright The Asahi Linux Contributors6 *7 * This driver adds support for two mailbox variants (called ASC and M3 by8 * Apple) found in Apple SoCs such as the M1. It consists of two FIFOs used to9 * exchange 64+32 bit messages between the main CPU and a co-processor.10 * Various coprocessors implement different IPC protocols based on these simple11 * messages and shared memory buffers.12 *13 * Both the main CPU and the co-processor see the same set of registers but14 * the first FIFO (A2I) is always used to transfer messages from the application15 * processor (us) to the I/O processor and the second one (I2A) for the16 * other direction.17 */18 19#include <linux/bitfield.h>20#include <linux/bits.h>21#include <linux/delay.h>22#include <linux/device.h>23#include <linux/interrupt.h>24#include <linux/io.h>25#include <linux/iopoll.h>26#include <linux/module.h>27#include <linux/of.h>28#include <linux/of_platform.h>29#include <linux/platform_device.h>30#include <linux/pm_runtime.h>31#include <linux/spinlock.h>32#include <linux/types.h>33#include "mailbox.h"34 35#define APPLE_ASC_MBOX_CONTROL_FULL BIT(16)36#define APPLE_ASC_MBOX_CONTROL_EMPTY BIT(17)37 38#define APPLE_ASC_MBOX_A2I_CONTROL 0x11039#define APPLE_ASC_MBOX_A2I_SEND0 0x80040#define APPLE_ASC_MBOX_A2I_SEND1 0x80841#define APPLE_ASC_MBOX_A2I_RECV0 0x81042#define APPLE_ASC_MBOX_A2I_RECV1 0x81843 44#define APPLE_ASC_MBOX_I2A_CONTROL 0x11445#define APPLE_ASC_MBOX_I2A_SEND0 0x82046#define APPLE_ASC_MBOX_I2A_SEND1 0x82847#define APPLE_ASC_MBOX_I2A_RECV0 0x83048#define APPLE_ASC_MBOX_I2A_RECV1 0x83849 50#define APPLE_M3_MBOX_CONTROL_FULL BIT(16)51#define APPLE_M3_MBOX_CONTROL_EMPTY BIT(17)52 53#define APPLE_M3_MBOX_A2I_CONTROL 0x5054#define APPLE_M3_MBOX_A2I_SEND0 0x6055#define APPLE_M3_MBOX_A2I_SEND1 0x6856#define APPLE_M3_MBOX_A2I_RECV0 0x7057#define APPLE_M3_MBOX_A2I_RECV1 0x7858 59#define APPLE_M3_MBOX_I2A_CONTROL 0x8060#define APPLE_M3_MBOX_I2A_SEND0 0x9061#define APPLE_M3_MBOX_I2A_SEND1 0x9862#define APPLE_M3_MBOX_I2A_RECV0 0xa063#define APPLE_M3_MBOX_I2A_RECV1 0xa864 65#define APPLE_M3_MBOX_IRQ_ENABLE 0x4866#define APPLE_M3_MBOX_IRQ_ACK 0x4c67#define APPLE_M3_MBOX_IRQ_A2I_EMPTY BIT(0)68#define APPLE_M3_MBOX_IRQ_A2I_NOT_EMPTY BIT(1)69#define APPLE_M3_MBOX_IRQ_I2A_EMPTY BIT(2)70#define APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY BIT(3)71 72#define APPLE_MBOX_MSG1_OUTCNT GENMASK(56, 52)73#define APPLE_MBOX_MSG1_INCNT GENMASK(51, 48)74#define APPLE_MBOX_MSG1_OUTPTR GENMASK(47, 44)75#define APPLE_MBOX_MSG1_INPTR GENMASK(43, 40)76#define APPLE_MBOX_MSG1_MSG GENMASK(31, 0)77 78#define APPLE_MBOX_TX_TIMEOUT 50079 80struct apple_mbox_hw {81 unsigned int control_full;82 unsigned int control_empty;83 84 unsigned int a2i_control;85 unsigned int a2i_send0;86 unsigned int a2i_send1;87 88 unsigned int i2a_control;89 unsigned int i2a_recv0;90 unsigned int i2a_recv1;91 92 bool has_irq_controls;93 unsigned int irq_enable;94 unsigned int irq_ack;95 unsigned int irq_bit_recv_not_empty;96 unsigned int irq_bit_send_empty;97};98 99int apple_mbox_send(struct apple_mbox *mbox, const struct apple_mbox_msg msg,100 bool atomic)101{102 unsigned long flags;103 int ret;104 u32 mbox_ctrl;105 long t;106 107 spin_lock_irqsave(&mbox->tx_lock, flags);108 mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control);109 110 while (mbox_ctrl & mbox->hw->control_full) {111 if (atomic) {112 ret = readl_poll_timeout_atomic(113 mbox->regs + mbox->hw->a2i_control, mbox_ctrl,114 !(mbox_ctrl & mbox->hw->control_full), 100,115 APPLE_MBOX_TX_TIMEOUT * 1000);116 117 if (ret) {118 spin_unlock_irqrestore(&mbox->tx_lock, flags);119 return ret;120 }121 122 break;123 }124 /*125 * The interrupt is level triggered and will keep firing as long as the126 * FIFO is empty. It will also keep firing if the FIFO was empty127 * at any point in the past until it has been acknowledged at the128 * mailbox level. By acknowledging it here we can ensure that we will129 * only get the interrupt once the FIFO has been cleared again.130 * If the FIFO is already empty before the ack it will fire again131 * immediately after the ack.132 */133 if (mbox->hw->has_irq_controls) {134 writel_relaxed(mbox->hw->irq_bit_send_empty,135 mbox->regs + mbox->hw->irq_ack);136 }137 enable_irq(mbox->irq_send_empty);138 reinit_completion(&mbox->tx_empty);139 spin_unlock_irqrestore(&mbox->tx_lock, flags);140 141 t = wait_for_completion_interruptible_timeout(142 &mbox->tx_empty,143 msecs_to_jiffies(APPLE_MBOX_TX_TIMEOUT));144 if (t < 0)145 return t;146 else if (t == 0)147 return -ETIMEDOUT;148 149 spin_lock_irqsave(&mbox->tx_lock, flags);150 mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control);151 }152 153 writeq_relaxed(msg.msg0, mbox->regs + mbox->hw->a2i_send0);154 writeq_relaxed(FIELD_PREP(APPLE_MBOX_MSG1_MSG, msg.msg1),155 mbox->regs + mbox->hw->a2i_send1);156 157 spin_unlock_irqrestore(&mbox->tx_lock, flags);158 159 return 0;160}161EXPORT_SYMBOL(apple_mbox_send);162 163static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)164{165 struct apple_mbox *mbox = data;166 167 /*168 * We don't need to acknowledge the interrupt at the mailbox level169 * here even if supported by the hardware. It will keep firing but that170 * doesn't matter since it's disabled at the main interrupt controller.171 * apple_mbox_send will acknowledge it before enabling172 * it at the main controller again.173 */174 spin_lock(&mbox->tx_lock);175 disable_irq_nosync(mbox->irq_send_empty);176 complete(&mbox->tx_empty);177 spin_unlock(&mbox->tx_lock);178 179 return IRQ_HANDLED;180}181 182static int apple_mbox_poll_locked(struct apple_mbox *mbox)183{184 struct apple_mbox_msg msg;185 int ret = 0;186 187 u32 mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->i2a_control);188 189 while (!(mbox_ctrl & mbox->hw->control_empty)) {190 msg.msg0 = readq_relaxed(mbox->regs + mbox->hw->i2a_recv0);191 msg.msg1 = FIELD_GET(192 APPLE_MBOX_MSG1_MSG,193 readq_relaxed(mbox->regs + mbox->hw->i2a_recv1));194 195 mbox->rx(mbox, msg, mbox->cookie);196 ret++;197 mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->i2a_control);198 }199 200 /*201 * The interrupt will keep firing even if there are no more messages202 * unless we also acknowledge it at the mailbox level here.203 * There's no race if a message comes in between the check in the while204 * loop above and the ack below: If a new messages arrives inbetween205 * those two the interrupt will just fire again immediately after the206 * ack since it's level triggered.207 */208 if (mbox->hw->has_irq_controls) {209 writel_relaxed(mbox->hw->irq_bit_recv_not_empty,210 mbox->regs + mbox->hw->irq_ack);211 }212 213 return ret;214}215 216static irqreturn_t apple_mbox_recv_irq(int irq, void *data)217{218 struct apple_mbox *mbox = data;219 220 spin_lock(&mbox->rx_lock);221 apple_mbox_poll_locked(mbox);222 spin_unlock(&mbox->rx_lock);223 224 return IRQ_HANDLED;225}226 227int apple_mbox_poll(struct apple_mbox *mbox)228{229 unsigned long flags;230 int ret;231 232 spin_lock_irqsave(&mbox->rx_lock, flags);233 ret = apple_mbox_poll_locked(mbox);234 spin_unlock_irqrestore(&mbox->rx_lock, flags);235 236 return ret;237}238EXPORT_SYMBOL(apple_mbox_poll);239 240int apple_mbox_start(struct apple_mbox *mbox)241{242 int ret;243 244 if (mbox->active)245 return 0;246 247 ret = pm_runtime_resume_and_get(mbox->dev);248 if (ret)249 return ret;250 251 /*252 * Only some variants of this mailbox HW provide interrupt control253 * at the mailbox level. We therefore need to handle enabling/disabling254 * interrupts at the main interrupt controller anyway for hardware that255 * doesn't. Just always keep the interrupts we care about enabled at256 * the mailbox level so that both hardware revisions behave almost257 * the same.258 */259 if (mbox->hw->has_irq_controls) {260 writel_relaxed(mbox->hw->irq_bit_recv_not_empty |261 mbox->hw->irq_bit_send_empty,262 mbox->regs + mbox->hw->irq_enable);263 }264 265 enable_irq(mbox->irq_recv_not_empty);266 mbox->active = true;267 return 0;268}269EXPORT_SYMBOL(apple_mbox_start);270 271void apple_mbox_stop(struct apple_mbox *mbox)272{273 if (!mbox->active)274 return;275 276 mbox->active = false;277 disable_irq(mbox->irq_recv_not_empty);278 pm_runtime_mark_last_busy(mbox->dev);279 pm_runtime_put_autosuspend(mbox->dev);280}281EXPORT_SYMBOL(apple_mbox_stop);282 283struct apple_mbox *apple_mbox_get(struct device *dev, int index)284{285 struct of_phandle_args args;286 struct platform_device *pdev;287 struct apple_mbox *mbox;288 int ret;289 290 ret = of_parse_phandle_with_args(dev->of_node, "mboxes", "#mbox-cells",291 index, &args);292 if (ret || !args.np)293 return ERR_PTR(ret);294 295 pdev = of_find_device_by_node(args.np);296 of_node_put(args.np);297 298 if (!pdev)299 return ERR_PTR(-EPROBE_DEFER);300 301 mbox = platform_get_drvdata(pdev);302 if (!mbox)303 return ERR_PTR(-EPROBE_DEFER);304 305 if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER))306 return ERR_PTR(-ENODEV);307 308 return mbox;309}310EXPORT_SYMBOL(apple_mbox_get);311 312struct apple_mbox *apple_mbox_get_byname(struct device *dev, const char *name)313{314 int index;315 316 index = of_property_match_string(dev->of_node, "mbox-names", name);317 if (index < 0)318 return ERR_PTR(index);319 320 return apple_mbox_get(dev, index);321}322EXPORT_SYMBOL(apple_mbox_get_byname);323 324static int apple_mbox_probe(struct platform_device *pdev)325{326 int ret;327 char *irqname;328 struct apple_mbox *mbox;329 struct device *dev = &pdev->dev;330 331 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);332 if (!mbox)333 return -ENOMEM;334 335 mbox->dev = &pdev->dev;336 mbox->hw = of_device_get_match_data(dev);337 if (!mbox->hw)338 return -EINVAL;339 340 mbox->regs = devm_platform_ioremap_resource(pdev, 0);341 if (IS_ERR(mbox->regs))342 return PTR_ERR(mbox->regs);343 344 mbox->irq_recv_not_empty =345 platform_get_irq_byname(pdev, "recv-not-empty");346 if (mbox->irq_recv_not_empty < 0)347 return -ENODEV;348 349 mbox->irq_send_empty = platform_get_irq_byname(pdev, "send-empty");350 if (mbox->irq_send_empty < 0)351 return -ENODEV;352 353 spin_lock_init(&mbox->rx_lock);354 spin_lock_init(&mbox->tx_lock);355 init_completion(&mbox->tx_empty);356 357 irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-recv", dev_name(dev));358 if (!irqname)359 return -ENOMEM;360 361 ret = devm_request_irq(dev, mbox->irq_recv_not_empty,362 apple_mbox_recv_irq,363 IRQF_NO_AUTOEN | IRQF_NO_SUSPEND, irqname, mbox);364 if (ret)365 return ret;366 367 irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-send", dev_name(dev));368 if (!irqname)369 return -ENOMEM;370 371 ret = devm_request_irq(dev, mbox->irq_send_empty,372 apple_mbox_send_empty_irq,373 IRQF_NO_AUTOEN | IRQF_NO_SUSPEND, irqname, mbox);374 if (ret)375 return ret;376 377 ret = devm_pm_runtime_enable(dev);378 if (ret)379 return ret;380 381 platform_set_drvdata(pdev, mbox);382 return 0;383}384 385static const struct apple_mbox_hw apple_mbox_asc_hw = {386 .control_full = APPLE_ASC_MBOX_CONTROL_FULL,387 .control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY,388 389 .a2i_control = APPLE_ASC_MBOX_A2I_CONTROL,390 .a2i_send0 = APPLE_ASC_MBOX_A2I_SEND0,391 .a2i_send1 = APPLE_ASC_MBOX_A2I_SEND1,392 393 .i2a_control = APPLE_ASC_MBOX_I2A_CONTROL,394 .i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0,395 .i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1,396 397 .has_irq_controls = false,398};399 400static const struct apple_mbox_hw apple_mbox_m3_hw = {401 .control_full = APPLE_M3_MBOX_CONTROL_FULL,402 .control_empty = APPLE_M3_MBOX_CONTROL_EMPTY,403 404 .a2i_control = APPLE_M3_MBOX_A2I_CONTROL,405 .a2i_send0 = APPLE_M3_MBOX_A2I_SEND0,406 .a2i_send1 = APPLE_M3_MBOX_A2I_SEND1,407 408 .i2a_control = APPLE_M3_MBOX_I2A_CONTROL,409 .i2a_recv0 = APPLE_M3_MBOX_I2A_RECV0,410 .i2a_recv1 = APPLE_M3_MBOX_I2A_RECV1,411 412 .has_irq_controls = true,413 .irq_enable = APPLE_M3_MBOX_IRQ_ENABLE,414 .irq_ack = APPLE_M3_MBOX_IRQ_ACK,415 .irq_bit_recv_not_empty = APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY,416 .irq_bit_send_empty = APPLE_M3_MBOX_IRQ_A2I_EMPTY,417};418 419static const struct of_device_id apple_mbox_of_match[] = {420 { .compatible = "apple,asc-mailbox-v4", .data = &apple_mbox_asc_hw },421 { .compatible = "apple,m3-mailbox-v2", .data = &apple_mbox_m3_hw },422 {}423};424MODULE_DEVICE_TABLE(of, apple_mbox_of_match);425 426static struct platform_driver apple_mbox_driver = {427 .driver = {428 .name = "apple-mailbox",429 .of_match_table = apple_mbox_of_match,430 },431 .probe = apple_mbox_probe,432};433module_platform_driver(apple_mbox_driver);434 435MODULE_LICENSE("Dual MIT/GPL");436MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");437MODULE_DESCRIPTION("Apple Mailbox driver");438