Skip to content

Commit b77a669

Browse files
SriMNvidianvidia-bfigg
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> (backported 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>
1 parent 037ef5d commit b77a669

1 file changed

Lines changed: 179 additions & 2 deletions

File tree

drivers/cxl/core/pci.c

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ static int cxl_reset_collect_sibling(struct pci_dev *func, void *data)
11411141
return 0;
11421142
}
11431143

1144-
static void __maybe_unused cxl_pci_functions_reset_prepare(struct cxl_reset_context *ctx)
1144+
static void cxl_pci_functions_reset_prepare(struct cxl_reset_context *ctx)
11451145
{
11461146
struct pci_dev *pdev = ctx->target;
11471147
struct cxl_reset_walk_ctx wctx;
@@ -1166,7 +1166,7 @@ static void __maybe_unused cxl_pci_functions_reset_prepare(struct cxl_reset_cont
11661166
}
11671167
}
11681168

1169-
static void __maybe_unused cxl_pci_functions_reset_done(struct cxl_reset_context *ctx)
1169+
static void cxl_pci_functions_reset_done(struct cxl_reset_context *ctx)
11701170
{
11711171
int i;
11721172

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

0 commit comments

Comments
 (0)