[PATCH 7/8] nvme: apple: Add driver for Apple NVMe storage controller
Simon Glass
sjg at chromium.org
Sat Jan 22 02:40:24 CET 2022
Hi Mark,
On Fri, 14 Jan 2022 at 04:05, Mark Kettenis <kettenis at openbsd.org> wrote:
>
> Add a driver for the NVMe storage controller integrated on
> Apple SoCs. This NVMe controller isn't PCI based and deviates
> from the NVMe standard in its implementation of the command
> submission queue and the integration of an NVMMU that needs
> to be managed. This commit tweaks the core NVMe code to
> support the linear command submission queue implemented by
> this controller. But setting up the submission queue and
> managing the NVMMU controller is handled by implementing
> the driver ops that were added in an earlier commit.
>
> Signed-off-by: Mark Kettenis <kettenis at openbsd.org>
> Tested-on: firefly-rk3399
> Tested-by: Mark Kettenis <kettenis at openbsd.org>
> ---
> configs/apple_m1_defconfig | 1 +
> drivers/nvme/Kconfig | 11 ++
> drivers/nvme/Makefile | 1 +
> drivers/nvme/nvme_apple.c | 233 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 246 insertions(+)
> create mode 100644 drivers/nvme/nvme_apple.c
Tested on: Macbook Air M1
Tested-by: Simon Glass <sjg at chromium.org>
>
> diff --git a/configs/apple_m1_defconfig b/configs/apple_m1_defconfig
> index cb235e4e7d..1528217b17 100644
> --- a/configs/apple_m1_defconfig
> +++ b/configs/apple_m1_defconfig
> @@ -11,6 +11,7 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y
> # CONFIG_NET is not set
> # CONFIG_MMC is not set
> CONFIG_DEBUG_UART_ANNOUNCE=y
> +CONFIG_NVME_APPLE=y
> CONFIG_USB_XHCI_HCD=y
> CONFIG_USB_XHCI_DWC3=y
> CONFIG_USB_KEYBOARD=y
> diff --git a/drivers/nvme/Kconfig b/drivers/nvme/Kconfig
> index 78da444c8b..0cb465160b 100644
> --- a/drivers/nvme/Kconfig
> +++ b/drivers/nvme/Kconfig
> @@ -10,6 +10,17 @@ config NVME
> This option enables support for NVM Express devices.
> It supports basic functions of NVMe (read/write).
>
> +config NVME_APPLE
> + bool "Apple NVMe controller support"
> + select NVME
> + help
> + This option enables support for the NVMe storage
> + controller integrated on Apple SoCs. This controller
> + isn't PCI-based based and deviates from the NVMe
> + standard implementation in its implementation of
> + the command submission queue and the integration
> + of an NVMMU that needs to be managed.
> +
> config NVME_PCI
> bool "NVM Express PCI device support"
> depends on PCI
> diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile
> index fad9724e17..fa7b619446 100644
> --- a/drivers/nvme/Makefile
> +++ b/drivers/nvme/Makefile
> @@ -3,4 +3,5 @@
> # Copyright (C) 2017, Bin Meng <bmeng.cn at gmail.com>
>
> obj-y += nvme-uclass.o nvme.o nvme_show.o
> +obj-$(CONFIG_NVME_APPLE) += nvme_apple.o
> obj-$(CONFIG_NVME_PCI) += nvme_pci.o
> diff --git a/drivers/nvme/nvme_apple.c b/drivers/nvme/nvme_apple.c
> new file mode 100644
> index 0000000000..b0dc8492f0
> --- /dev/null
> +++ b/drivers/nvme/nvme_apple.c
> @@ -0,0 +1,233 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2021 Mark Kettenis <kettenis at openbsd.org>
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <mailbox.h>
> +#include <mapmem.h>
> +#include "nvme.h"
> +#include <reset.h>
> +
> +#include <asm/io.h>
> +#include <asm/arch-apple/rtkit.h>
asm/arch/ should work
> +#include <linux/iopoll.h>
> +
> +#undef readl_poll_timeout
> +#define readl_poll_timeout readl_poll_sleep_timeout
Do we need this?
> +
> +#define REG_CPU_CTRL 0x0044
> +#define REG_CPU_CTRL_RUN BIT(4)
> +
> +#define ANS_MAX_PEND_CMDS_CTRL 0x01210
> +#define ANS_MAX_QUEUE_DEPTH 64
> +#define ANS_BOOT_STATUS 0x01300
> +#define ANS_BOOT_STATUS_OK 0xde71ce55
> +#define ANS_MODESEL 0x01304
> +#define ANS_UNKNOWN_CTRL 0x24008
> +#define ANS_PRP_NULL_CHECK (1 << 11)
> +#define ANS_LINEAR_SQ_CTRL 0x24908
> +#define ANS_LINEAR_SQ_CTRL_EN (1 << 0)
> +#define ANS_ASQ_DB 0x2490c
> +#define ANS_IOSQ_DB 0x24910
> +#define ANS_NVMMU_NUM 0x28100
> +#define ANS_NVMMU_BASE_ASQ 0x28108
> +#define ANS_NVMMU_BASE_IOSQ 0x28110
> +#define ANS_NVMMU_TCB_INVAL 0x28118
> +#define ANS_NVMMU_TCB_STAT 0x28120
> +
> +#define ANS_NVMMU_TCB_SIZE 0x4000
> +#define ANS_NVMMU_TCB_PITCH 0x80
> +
> +struct ans_nvmmu_tcb {
> + u8 opcode;
> + u8 flags;
> + u8 command_id;
> + u8 pad0;
> + u32 prpl_len;
> + u8 pad1[16];
> + u64 prp1;
> + u64 prp2;
Needs comments
> +};
> +
> +#define ANS_NVMMU_TCB_WRITE BIT(0)
> +#define ANS_NVMMU_TCB_READ BIT(1)
> +
> +struct apple_nvme_priv {
> + struct nvme_dev ndev;
> + void *base;
> + void *asc;
> + struct reset_ctl_bulk resets;
> + struct mbox_chan chan;
> + struct ans_nvmmu_tcb *tcbs[NVME_Q_NUM];
> + u32 __iomem *q_db[NVME_Q_NUM];
Needs comments
> +};
> +
> +static int apple_nvme_alloc_queue(struct nvme_queue *nvmeq)
> +{
> + struct apple_nvme_priv *priv =
> + container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
> + struct nvme_dev *dev = nvmeq->dev;
> +
> + switch (nvmeq->qid) {
> + case NVME_ADMIN_Q:
> + case NVME_IO_Q:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + priv->tcbs[nvmeq->qid] = (void *)memalign(4096, ANS_NVMMU_TCB_SIZE);
> + memset((void *)priv->tcbs[nvmeq->qid], 0, ANS_NVMMU_TCB_SIZE);
> +
> + switch (nvmeq->qid) {
> + case NVME_ADMIN_Q:
> + priv->q_db[nvmeq->qid] =
> + ((void __iomem *)dev->bar) + ANS_ASQ_DB;
> + nvme_writeq((ulong)priv->tcbs[nvmeq->qid],
> + ((void __iomem *)dev->bar) + ANS_NVMMU_BASE_ASQ);
> + break;
> + case NVME_IO_Q:
> + priv->q_db[nvmeq->qid] =
> + ((void __iomem *)dev->bar) + ANS_IOSQ_DB;
> + nvme_writeq((ulong)priv->tcbs[nvmeq->qid],
> + ((void __iomem *)dev->bar) + ANS_NVMMU_BASE_IOSQ);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static void apple_nvme_submit_cmd(struct nvme_queue *nvmeq,
> + struct nvme_command *cmd)
> +{
> + struct apple_nvme_priv *priv =
> + container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
> + struct ans_nvmmu_tcb *tcb;
> + u16 tail = nvmeq->sq_tail;
> +
> + tcb = ((void *)priv->tcbs[nvmeq->qid]) + tail * ANS_NVMMU_TCB_PITCH;
> + memset(tcb, 0, sizeof(*tcb));
> + tcb->opcode = cmd->common.opcode;
> + tcb->flags = ANS_NVMMU_TCB_WRITE | ANS_NVMMU_TCB_READ;
> + tcb->command_id = tail;
> + tcb->prpl_len = cmd->rw.length;
> + tcb->prp1 = cmd->common.prp1;
> + tcb->prp2 = cmd->common.prp2;
> +
> + writel(tail, priv->q_db[nvmeq->qid]);
> +}
> +
> +static void apple_nvme_complete_cmd(struct nvme_queue *nvmeq,
> + struct nvme_command *cmd)
> +{
> + struct apple_nvme_priv *priv =
> + container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
> + struct ans_nvmmu_tcb *tcb;
> + u16 tail = nvmeq->sq_tail;
> +
> + tcb = ((void *)priv->tcbs[nvmeq->qid]) + tail * ANS_NVMMU_TCB_PITCH;
> + memset(tcb, 0, sizeof(*tcb));
> + writel(tail, ((void __iomem *)nvmeq->dev->bar) + ANS_NVMMU_TCB_INVAL);
> + readl(((void __iomem *)nvmeq->dev->bar) + ANS_NVMMU_TCB_STAT);
> +
> + if (++tail == nvmeq->q_depth)
> + tail = 0;
> + nvmeq->sq_tail = tail;
> +}
> +
> +static int apple_nvme_probe(struct udevice *dev)
> +{
> + struct apple_nvme_priv *priv = dev_get_priv(dev);
> + fdt_addr_t addr;
> + u32 ctrl, stat;
> + int ret;
> +
> + priv->base = dev_read_addr_ptr(dev);
> + if (!priv->base)
> + return -EINVAL;
> +
> + addr = dev_read_addr_index(dev, 1);
> + if (addr == FDT_ADDR_T_NONE)
> + return -EINVAL;
> + priv->asc = map_sysmem(addr, 0);
> +
> + ret = reset_get_bulk(dev, &priv->resets);
> + if (ret < 0)
> + return ret;
> +
> + ret = mbox_get_by_index(dev, 0, &priv->chan);
Is the mailbox not mentioned in the device tre?
> + if (ret < 0)
> + return ret;
> +
> + ctrl = readl(priv->asc + REG_CPU_CTRL);
> + writel(ctrl | REG_CPU_CTRL_RUN, priv->asc + REG_CPU_CTRL);
> +
> + ret = apple_rtkit_init(&priv->chan);
Should probe a driver here
> + if (ret < 0)
> + return ret;
> +
> + ret = readl_poll_timeout(priv->base + ANS_BOOT_STATUS, stat,
> + (stat == ANS_BOOT_STATUS_OK), 100, 500000);
> + if (ret < 0) {
> + printf("%s: NVMe firmware didn't boot\n", __func__);
> + return -ETIMEDOUT;
> + }
> +
> + writel(ANS_LINEAR_SQ_CTRL_EN, priv->base + ANS_LINEAR_SQ_CTRL);
> + writel(((ANS_MAX_QUEUE_DEPTH << 16) | ANS_MAX_QUEUE_DEPTH),
> + priv->base + ANS_MAX_PEND_CMDS_CTRL);
> +
> + writel(readl(priv->base + ANS_UNKNOWN_CTRL) & ~ANS_PRP_NULL_CHECK,
> + priv->base + ANS_UNKNOWN_CTRL);
> +
> + strcpy(priv->ndev.vendor, "Apple");
> +
> + writel((ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH) - 1,
> + priv->base + ANS_NVMMU_NUM);
> + writel(0, priv->base + ANS_MODESEL);
> +
> + priv->ndev.bar = priv->base;
> + return nvme_init(dev);
> +}
> +
> +static int apple_nvme_remove(struct udevice *dev)
> +{
> + struct apple_nvme_priv *priv = dev_get_priv(dev);
> + u32 ctrl;
> +
> + nvme_shutdown(dev);
> +
> + apple_rtkit_shutdown(&priv->chan, APPLE_RTKIT_PWR_STATE_SLEEP);
> +
> + ctrl = readl(priv->asc + REG_CPU_CTRL);
> + writel(ctrl & ~REG_CPU_CTRL_RUN, priv->asc + REG_CPU_CTRL);
> +
> + reset_assert_bulk(&priv->resets);
> + reset_deassert_bulk(&priv->resets);
> +
> + return 0;
> +}
> +
> +static const struct nvme_ops apple_nvme_ops = {
> + .alloc_queue = apple_nvme_alloc_queue,
> + .submit_cmd = apple_nvme_submit_cmd,
> + .complete_cmd = apple_nvme_complete_cmd,
> +};
> +
> +static const struct udevice_id apple_nvme_ids[] = {
> + { .compatible = "apple,nvme-ans2" },
> + { /* sentinel */ }
> +};
> +
> +U_BOOT_DRIVER(apple_nvme) = {
> + .name = "apple_nvme",
> + .id = UCLASS_NVME,
> + .of_match = apple_nvme_ids,
> + .priv_auto = sizeof(struct apple_nvme_priv),
> + .probe = apple_nvme_probe,
> + .remove = apple_nvme_remove,
> + .ops = &apple_nvme_ops,
> + .flags = DM_FLAG_OS_PREPARE,
> +};
> --
> 2.34.1
>
Regards,
Simon
More information about the U-Boot
mailing list