Skip to content

Commit 6a4cd40

Browse files
SriMNvidiakobak2026
authored andcommitted
NVIDIA: VR: SAUCE: cxl: Add CXL DVSEC reset sequence and flow orchestration
BugLink: https://bugs.launchpad.net/bugs/2143032 cxl_dev_reset() implements the hardware reset sequence: optionally enable memory clear, initiate reset via CTRL2, wait for completion, and re-enable caching. cxl_do_reset() orchestrates the full reset flow: 1. CXL pre-reset: mem offlining and cache flush (when memdev present) 2. PCI save/disable: pci_dev_save_and_disable() automatically saves CXL DVSEC and HDM decoder state via PCI core hooks 3. Sibling coordination: save/disable CXL.cachemem sibling functions 4. Execute CXL DVSEC reset 5. Sibling restore: always runs to re-enable sibling functions 6. PCI restore: pci_dev_restore() automatically restores CXL state The CXL-specific DVSEC and HDM save/restore is handled by the PCI core's CXL save/restore infrastructure (drivers/pci/cxl.c). Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com> (cherry picked from https://lore.kernel.org/linux-cxl/20260306092322.148765-1-smadhavan@nvidia.com/) Signed-off-by: Jiandi An <jan@nvidia.com> Acked-by: Jamie Nguyen <jamien@nvidia.com> Acked-by: Nirmoy Das <nirmoyd@nvidia.com> Acked-by: Carol L Soto <csoto@nvidia.com> Acked-by: Matthew R. Ochs <mochs@nvidia.com> Signed-off-by: Brad Figg <bfigg@nvidia.com> (backported from commit 92fb807 nv-kernels/24.04_linux-nvidia-6.17-next) [koba: Treat error-valued cxlmd->endpoint as no endpoint to avoid dereferencing ERR_PTR before endpoint attach.] [koba: Check sibling collection failure before starting the CXL reset so allocation failure restores the target and aborts.] [koba: Limit the memdev device lock to endpoint-dependent memory preparation and cache flush, restore memory quiesce before PCI disable, and track sibling reset preparation so reset_done cleanup only runs after successful sibling prepare.] Signed-off-by: Koba Ko <kobak@nvidia.com>
1 parent 67e4b5a commit 6a4cd40

1 file changed

Lines changed: 192 additions & 4 deletions

File tree

drivers/cxl/core/pci.c

Lines changed: 192 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,7 @@ static int __maybe_unused cxl_reset_prepare_memdev(struct cxl_memdev *cxlmd)
10051005

10061006
dev = cxlmd->cxlds->dev;
10071007
endpoint = cxlmd->endpoint;
1008-
if (!endpoint)
1008+
if (!endpoint || IS_ERR(endpoint))
10091009
return 0;
10101010

10111011
return device_for_each_child(&endpoint->dev, NULL,
@@ -1149,7 +1149,7 @@ static int cxl_reset_collect_sibling(struct pci_dev *func, void *data)
11491149
return 0;
11501150
}
11511151

1152-
static void __maybe_unused cxl_pci_functions_reset_release(struct cxl_reset_context *ctx)
1152+
static void cxl_pci_functions_reset_release(struct cxl_reset_context *ctx)
11531153
{
11541154
int i;
11551155

@@ -1161,7 +1161,7 @@ static void __maybe_unused cxl_pci_functions_reset_release(struct cxl_reset_cont
11611161
ctx->pci_func_cap = 0;
11621162
}
11631163

1164-
static int __maybe_unused cxl_pci_functions_reset_prepare(struct cxl_reset_context *ctx)
1164+
static int cxl_pci_functions_reset_prepare(struct cxl_reset_context *ctx)
11651165
{
11661166
struct pci_dev *pdev = ctx->target;
11671167
struct cxl_reset_walk_ctx wctx;
@@ -1193,7 +1193,7 @@ static int __maybe_unused cxl_pci_functions_reset_prepare(struct cxl_reset_conte
11931193
return 0;
11941194
}
11951195

1196-
static void __maybe_unused cxl_pci_functions_reset_done(struct cxl_reset_context *ctx)
1196+
static void cxl_pci_functions_reset_done(struct cxl_reset_context *ctx)
11971197
{
11981198
int i;
11991199

@@ -1203,3 +1203,191 @@ static void __maybe_unused cxl_pci_functions_reset_done(struct cxl_reset_context
12031203
}
12041204
cxl_pci_functions_reset_release(ctx);
12051205
}
1206+
1207+
/*
1208+
* CXL device reset execution
1209+
*/
1210+
static int cxl_dev_reset(struct pci_dev *pdev, int dvsec)
1211+
{
1212+
static const u32 reset_timeout_ms[] = { 10, 100, 1000, 10000, 100000 };
1213+
u16 cap, ctrl2, status2;
1214+
u32 timeout_ms;
1215+
int rc, idx;
1216+
1217+
if (!pci_wait_for_pending_transaction(pdev))
1218+
pci_err(pdev, "timed out waiting for pending transactions\n");
1219+
1220+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CAP, &cap);
1221+
if (rc)
1222+
return rc;
1223+
1224+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2, &ctrl2);
1225+
if (rc)
1226+
return rc;
1227+
1228+
/*
1229+
* Disable caching and initiate cache writeback+invalidation if the
1230+
* device supports it. Poll for completion.
1231+
* Per CXL r3.2 section 9.6, software may use the cache size from
1232+
* DVSEC CXL Capability2 to compute a suitable timeout; we use a
1233+
* default of 10ms.
1234+
*/
1235+
if (cap & PCI_DVSEC_CXL_CACHE_WBI_CAPABLE) {
1236+
u32 wbi_poll_us = 100;
1237+
s32 wbi_remaining_us = 10000;
1238+
1239+
ctrl2 |= PCI_DVSEC_CXL_DISABLE_CACHING;
1240+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2,
1241+
ctrl2);
1242+
if (rc)
1243+
return rc;
1244+
1245+
ctrl2 |= PCI_DVSEC_CXL_INIT_CACHE_WBI;
1246+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2,
1247+
ctrl2);
1248+
if (rc)
1249+
return rc;
1250+
1251+
do {
1252+
usleep_range(wbi_poll_us, wbi_poll_us + 1);
1253+
wbi_remaining_us -= wbi_poll_us;
1254+
rc = pci_read_config_word(pdev,
1255+
dvsec + PCI_DVSEC_CXL_STATUS2,
1256+
&status2);
1257+
if (rc)
1258+
return rc;
1259+
} while (!(status2 & PCI_DVSEC_CXL_CACHE_INV) &&
1260+
wbi_remaining_us > 0);
1261+
1262+
if (!(status2 & PCI_DVSEC_CXL_CACHE_INV)) {
1263+
pci_err(pdev, "CXL cache WB+I timed out\n");
1264+
return -ETIMEDOUT;
1265+
}
1266+
} else if (cap & PCI_DVSEC_CXL_CACHE_CAPABLE) {
1267+
ctrl2 |= PCI_DVSEC_CXL_DISABLE_CACHING;
1268+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2,
1269+
ctrl2);
1270+
if (rc)
1271+
return rc;
1272+
}
1273+
1274+
if (cap & PCI_DVSEC_CXL_RST_MEM_CLR_CAPABLE) {
1275+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2,
1276+
&ctrl2);
1277+
if (rc)
1278+
return rc;
1279+
1280+
ctrl2 |= PCI_DVSEC_CXL_RST_MEM_CLR_EN;
1281+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2,
1282+
ctrl2);
1283+
if (rc)
1284+
return rc;
1285+
}
1286+
1287+
idx = FIELD_GET(PCI_DVSEC_CXL_RST_TIMEOUT, cap);
1288+
if (idx >= ARRAY_SIZE(reset_timeout_ms))
1289+
idx = ARRAY_SIZE(reset_timeout_ms) - 1;
1290+
timeout_ms = reset_timeout_ms[idx];
1291+
1292+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2, &ctrl2);
1293+
if (rc)
1294+
return rc;
1295+
1296+
ctrl2 |= PCI_DVSEC_CXL_INIT_CXL_RST;
1297+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2, ctrl2);
1298+
if (rc)
1299+
return rc;
1300+
1301+
msleep(timeout_ms);
1302+
1303+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_STATUS2,
1304+
&status2);
1305+
if (rc)
1306+
return rc;
1307+
1308+
if (status2 & PCI_DVSEC_CXL_RST_ERR) {
1309+
pci_err(pdev, "CXL reset error\n");
1310+
return -EIO;
1311+
}
1312+
1313+
if (!(status2 & PCI_DVSEC_CXL_RST_DONE)) {
1314+
pci_err(pdev, "CXL reset timeout\n");
1315+
return -ETIMEDOUT;
1316+
}
1317+
1318+
rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2, &ctrl2);
1319+
if (rc)
1320+
return rc;
1321+
1322+
ctrl2 &= ~PCI_DVSEC_CXL_DISABLE_CACHING;
1323+
rc = pci_write_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL2, ctrl2);
1324+
if (rc)
1325+
return rc;
1326+
1327+
return 0;
1328+
}
1329+
1330+
static int match_memdev_by_parent(struct device *dev, const void *parent)
1331+
{
1332+
return is_cxl_memdev(dev) && dev->parent == parent;
1333+
}
1334+
1335+
static int __cxl_do_reset(struct pci_dev *pdev, struct cxl_memdev *cxlmd,
1336+
int dvsec)
1337+
{
1338+
struct cxl_reset_context ctx = { .target = pdev };
1339+
bool siblings_prepared = false;
1340+
int rc;
1341+
1342+
mutex_lock(&cxl_reset_mutex);
1343+
pci_dev_lock(pdev);
1344+
1345+
if (cxlmd) {
1346+
guard(device)(&cxlmd->dev);
1347+
1348+
rc = cxl_reset_prepare_memdev(cxlmd);
1349+
if (rc)
1350+
goto out_unlock;
1351+
1352+
cxl_reset_flush_cpu_caches(cxlmd);
1353+
}
1354+
1355+
pci_dev_save_and_disable(pdev);
1356+
1357+
rc = cxl_pci_functions_reset_prepare(&ctx);
1358+
if (!rc) {
1359+
siblings_prepared = true;
1360+
rc = cxl_dev_reset(pdev, dvsec);
1361+
}
1362+
1363+
if (siblings_prepared)
1364+
cxl_pci_functions_reset_done(&ctx);
1365+
1366+
pci_dev_restore(pdev);
1367+
1368+
out_unlock:
1369+
pci_dev_unlock(pdev);
1370+
mutex_unlock(&cxl_reset_mutex);
1371+
1372+
return rc;
1373+
}
1374+
1375+
static int cxl_do_reset(struct pci_dev *pdev)
1376+
{
1377+
int dvsec;
1378+
1379+
dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL,
1380+
PCI_DVSEC_CXL_DEVICE);
1381+
if (!dvsec)
1382+
return -ENODEV;
1383+
1384+
struct device *memdev __free(put_device) =
1385+
bus_find_device(&cxl_bus_type, NULL, &pdev->dev,
1386+
match_memdev_by_parent);
1387+
if (!memdev)
1388+
return __cxl_do_reset(pdev, NULL, dvsec);
1389+
1390+
struct cxl_memdev *cxlmd = to_cxl_memdev(memdev);
1391+
1392+
return __cxl_do_reset(pdev, cxlmd, dvsec);
1393+
}

0 commit comments

Comments
 (0)