blob: d9d491c2be11d0474eef8e6a5a7fa0c98481072f [file] [log] [blame]
Mark Kettenis31d5f7b2022-01-22 20:38:18 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2021 Mark Kettenis <kettenis@openbsd.org>
4 */
5
6#include <common.h>
7#include <dm.h>
8#include <mailbox.h>
9#include <mapmem.h>
10#include "nvme.h"
11#include <reset.h>
12
13#include <asm/io.h>
14#include <asm/arch/rtkit.h>
15#include <linux/iopoll.h>
16
17/* ASC registers */
18#define REG_CPU_CTRL 0x0044
19#define REG_CPU_CTRL_RUN BIT(4)
20
21/* Apple NVMe registers */
22#define ANS_MAX_PEND_CMDS_CTRL 0x01210
23#define ANS_MAX_QUEUE_DEPTH 64
24#define ANS_BOOT_STATUS 0x01300
25#define ANS_BOOT_STATUS_OK 0xde71ce55
26#define ANS_MODESEL 0x01304
27#define ANS_UNKNOWN_CTRL 0x24008
28#define ANS_PRP_NULL_CHECK (1 << 11)
29#define ANS_LINEAR_SQ_CTRL 0x24908
30#define ANS_LINEAR_SQ_CTRL_EN (1 << 0)
31#define ANS_ASQ_DB 0x2490c
32#define ANS_IOSQ_DB 0x24910
33#define ANS_NVMMU_NUM 0x28100
34#define ANS_NVMMU_BASE_ASQ 0x28108
35#define ANS_NVMMU_BASE_IOSQ 0x28110
36#define ANS_NVMMU_TCB_INVAL 0x28118
37#define ANS_NVMMU_TCB_STAT 0x28120
38
39#define ANS_NVMMU_TCB_SIZE 0x4000
40#define ANS_NVMMU_TCB_PITCH 0x80
41
42/*
43 * The Apple NVMe controller includes an IOMMU known as NVMMU. The
44 * NVMMU is programmed through an array of TCBs. These TCBs are paired
45 * with the corresponding slot in the submission queues and need to be
46 * configured with the command details before a command is allowed to
47 * execute. This is necessary even for commands that don't do DMA.
48 */
49struct ans_nvmmu_tcb {
50 u8 opcode;
51 u8 flags;
52 u8 slot;
53 u8 pad0;
54 u32 prpl_len;
55 u8 pad1[16];
56 u64 prp1;
57 u64 prp2;
58};
59
60#define ANS_NVMMU_TCB_WRITE BIT(0)
61#define ANS_NVMMU_TCB_READ BIT(1)
62
63struct apple_nvme_priv {
64 struct nvme_dev ndev;
65 void *base; /* NVMe registers */
66 void *asc; /* ASC registers */
67 struct reset_ctl_bulk resets; /* ASC reset */
68 struct mbox_chan chan;
69 struct ans_nvmmu_tcb *tcbs[NVME_Q_NUM]; /* Submission queue TCBs */
70 u32 __iomem *q_db[NVME_Q_NUM]; /* Submission queue doorbell */
71};
72
73static int apple_nvme_setup_queue(struct nvme_queue *nvmeq)
74{
75 struct apple_nvme_priv *priv =
76 container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
77 struct nvme_dev *dev = nvmeq->dev;
78
79 switch (nvmeq->qid) {
80 case NVME_ADMIN_Q:
81 case NVME_IO_Q:
82 break;
83 default:
84 return -EINVAL;
85 }
86
87 priv->tcbs[nvmeq->qid] = (void *)memalign(4096, ANS_NVMMU_TCB_SIZE);
88 memset((void *)priv->tcbs[nvmeq->qid], 0, ANS_NVMMU_TCB_SIZE);
89
90 switch (nvmeq->qid) {
91 case NVME_ADMIN_Q:
92 priv->q_db[nvmeq->qid] =
93 ((void __iomem *)dev->bar) + ANS_ASQ_DB;
94 nvme_writeq((ulong)priv->tcbs[nvmeq->qid],
95 ((void __iomem *)dev->bar) + ANS_NVMMU_BASE_ASQ);
96 break;
97 case NVME_IO_Q:
98 priv->q_db[nvmeq->qid] =
99 ((void __iomem *)dev->bar) + ANS_IOSQ_DB;
100 nvme_writeq((ulong)priv->tcbs[nvmeq->qid],
101 ((void __iomem *)dev->bar) + ANS_NVMMU_BASE_IOSQ);
102 break;
103 }
104
105 return 0;
106}
107
108static void apple_nvme_submit_cmd(struct nvme_queue *nvmeq,
109 struct nvme_command *cmd)
110{
111 struct apple_nvme_priv *priv =
112 container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
113 struct ans_nvmmu_tcb *tcb;
114 u16 tail = nvmeq->sq_tail;
115
116 tcb = ((void *)priv->tcbs[nvmeq->qid]) + tail * ANS_NVMMU_TCB_PITCH;
117 memset(tcb, 0, sizeof(*tcb));
118 tcb->opcode = cmd->common.opcode;
119 tcb->flags = ANS_NVMMU_TCB_WRITE | ANS_NVMMU_TCB_READ;
120 tcb->slot = tail;
121 tcb->prpl_len = cmd->rw.length;
122 tcb->prp1 = cmd->common.prp1;
123 tcb->prp2 = cmd->common.prp2;
124
125 writel(tail, priv->q_db[nvmeq->qid]);
126}
127
128static void apple_nvme_complete_cmd(struct nvme_queue *nvmeq,
129 struct nvme_command *cmd)
130{
131 struct apple_nvme_priv *priv =
132 container_of(nvmeq->dev, struct apple_nvme_priv, ndev);
133 struct ans_nvmmu_tcb *tcb;
134 u16 tail = nvmeq->sq_tail;
135
136 tcb = ((void *)priv->tcbs[nvmeq->qid]) + tail * ANS_NVMMU_TCB_PITCH;
137 memset(tcb, 0, sizeof(*tcb));
138 writel(tail, ((void __iomem *)nvmeq->dev->bar) + ANS_NVMMU_TCB_INVAL);
139 readl(((void __iomem *)nvmeq->dev->bar) + ANS_NVMMU_TCB_STAT);
140
141 if (++tail == nvmeq->q_depth)
142 tail = 0;
143 nvmeq->sq_tail = tail;
144}
145
146static int apple_nvme_probe(struct udevice *dev)
147{
148 struct apple_nvme_priv *priv = dev_get_priv(dev);
149 fdt_addr_t addr;
150 u32 ctrl, stat;
151 int ret;
152
153 priv->base = dev_read_addr_ptr(dev);
154 if (!priv->base)
155 return -EINVAL;
156
157 addr = dev_read_addr_index(dev, 1);
158 if (addr == FDT_ADDR_T_NONE)
159 return -EINVAL;
160 priv->asc = map_sysmem(addr, 0);
161
162 ret = reset_get_bulk(dev, &priv->resets);
163 if (ret < 0)
164 return ret;
165
166 ret = mbox_get_by_index(dev, 0, &priv->chan);
167 if (ret < 0)
168 return ret;
169
170 ctrl = readl(priv->asc + REG_CPU_CTRL);
171 writel(ctrl | REG_CPU_CTRL_RUN, priv->asc + REG_CPU_CTRL);
172
173 ret = apple_rtkit_init(&priv->chan);
174 if (ret < 0)
175 return ret;
176
177 ret = readl_poll_sleep_timeout(priv->base + ANS_BOOT_STATUS, stat,
178 (stat == ANS_BOOT_STATUS_OK), 100,
179 500000);
180 if (ret < 0) {
181 printf("%s: NVMe firmware didn't boot\n", __func__);
182 return -ETIMEDOUT;
183 }
184
185 writel(ANS_LINEAR_SQ_CTRL_EN, priv->base + ANS_LINEAR_SQ_CTRL);
186 writel(((ANS_MAX_QUEUE_DEPTH << 16) | ANS_MAX_QUEUE_DEPTH),
187 priv->base + ANS_MAX_PEND_CMDS_CTRL);
188
189 writel(readl(priv->base + ANS_UNKNOWN_CTRL) & ~ANS_PRP_NULL_CHECK,
190 priv->base + ANS_UNKNOWN_CTRL);
191
192 strcpy(priv->ndev.vendor, "Apple");
193
194 writel((ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH) - 1,
195 priv->base + ANS_NVMMU_NUM);
196 writel(0, priv->base + ANS_MODESEL);
197
198 priv->ndev.bar = priv->base;
199 return nvme_init(dev);
200}
201
202static int apple_nvme_remove(struct udevice *dev)
203{
204 struct apple_nvme_priv *priv = dev_get_priv(dev);
205 u32 ctrl;
206
207 nvme_shutdown(dev);
208
209 apple_rtkit_shutdown(&priv->chan, APPLE_RTKIT_PWR_STATE_SLEEP);
210
211 ctrl = readl(priv->asc + REG_CPU_CTRL);
212 writel(ctrl & ~REG_CPU_CTRL_RUN, priv->asc + REG_CPU_CTRL);
213
214 reset_assert_bulk(&priv->resets);
215 reset_deassert_bulk(&priv->resets);
216
217 return 0;
218}
219
220static const struct nvme_ops apple_nvme_ops = {
221 .setup_queue = apple_nvme_setup_queue,
222 .submit_cmd = apple_nvme_submit_cmd,
223 .complete_cmd = apple_nvme_complete_cmd,
224};
225
226static const struct udevice_id apple_nvme_ids[] = {
227 { .compatible = "apple,nvme-ans2" },
228 { /* sentinel */ }
229};
230
231U_BOOT_DRIVER(apple_nvme) = {
232 .name = "apple_nvme",
233 .id = UCLASS_NVME,
234 .of_match = apple_nvme_ids,
235 .priv_auto = sizeof(struct apple_nvme_priv),
236 .probe = apple_nvme_probe,
237 .remove = apple_nvme_remove,
238 .ops = &apple_nvme_ops,
239 .flags = DM_FLAG_OS_PREPARE,
240};