[PATCH 7/8] nvme: apple: Add driver for Apple NVMe storage controller
Mark Kettenis
mark.kettenis at xs4all.nl
Sat Jan 22 15:45:10 CET 2022
> From: Simon Glass <sjg at chromium.org>
> Date: Fri, 21 Jan 2022 18:40:24 -0700
>
> 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
It doesn't. For some reason I end up with
arch/arm/include/asm/arch -> arch-m1
But this rtkit stuff is expected to be generic and apply to all Apple
SoCs, not just the M1.
>
> > +#include <linux/iopoll.h>
> > +
> > +#undef readl_poll_timeout
> > +#define readl_poll_timeout readl_poll_sleep_timeout
>
> Do we need this?
No we don't. Fixed.
> > +
> > +#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
I put a comment at the top explaining my limited understanding of what
the TCBs are used for.
>
> > +};
> > +
> > +#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
Done, although they give me a captain obvious vibe...
> > +};
> > +
> > +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?
There is an "mboxes" property, which is what mbox_get_by_index() uses.
But there is only one mbox so it isn't named.
>
> > + 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
See my reply to PATCH 3/8 in this series.
> > + 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