brintos

brintos / linux-shallow public Read only

0
0
Text · 11.7 KiB · 49a0955 Raw
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