Skip to content

Commit b246a3b

Browse files
awilliams-marvellSomasundaram Krishnasamy
authored andcommitted
octeontx2: mmc: Add tuning support for HS400 mode
Tuning the data input is required for reliable HS400 operation on the Octeon TX2 SoCs. Often the sweet spot for HS200 mode does not work in HS400 mode and the eMMC spec provides no method of performing tuning. Instead of using the eMMC's tuning functionality this relies instead on a block specified in the device tree to contain a specific pattern designed for worst-case signalling. If the block does not contain the correct data it will be overwritten with the tuning pattern. The device tree must make sure that the chosen block does not interfere with any partitioning or filesystems. Generally block 1 is used which is fine as long as EFI partitioning is not used. The tuning code repeatedly reads this block with every tap value then chooses the midpoint of the longest successful run like how tuning is performed in HS200 mode for data in. This tuning method is ignored if the appropriate device tree entry is missing, instead falling back on the HS200 tuning. Change-Id: Ic3ddc17fbf023e6a5b2a951e2303daad28d95267 Signed-off-by: Aaron Williams <[email protected]> Reviewed-on: https://sj1git1.cavium.com/23016 Reviewed-by: Chandrakala Chavva <[email protected]> Tested-by: sa_ip-sw-jenkins <[email protected]> Reviewed-on: https://sj1git1.cavium.com/23929 Reviewed-by: Sunil Kovvuri Goutham <[email protected]> Reviewed-on: https://sj1git1.cavium.com/24029 Tested-by: Sunil Kovvuri Goutham <[email protected]> Jira: LINUX-4695 Orabug: 30969935 Signed-off-by: Dave Kleikamp <[email protected]> Reviewed-by: Karl Volz <[email protected]> Signed-off-by: Somasundaram Krishnasamy <[email protected]>
1 parent 773e639 commit b246a3b

File tree

2 files changed

+312
-0
lines changed

2 files changed

+312
-0
lines changed

drivers/mmc/host/cavium.c

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,307 @@ static int adjust_tuning(struct mmc_host *mmc, struct adj *adj, u32 opcode)
14231423
return 0;
14241424
}
14251425

1426+
static const u8 octeontx_hs400_tuning_block[512] = {
1427+
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
1428+
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
1429+
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
1430+
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
1431+
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
1432+
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
1433+
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
1434+
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
1435+
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
1436+
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
1437+
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
1438+
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
1439+
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
1440+
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
1441+
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
1442+
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
1443+
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
1444+
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
1445+
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
1446+
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
1447+
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
1448+
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
1449+
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
1450+
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
1451+
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
1452+
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
1453+
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
1454+
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
1455+
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
1456+
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
1457+
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
1458+
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
1459+
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
1460+
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
1461+
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
1462+
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
1463+
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
1464+
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
1465+
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
1466+
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
1467+
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
1468+
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
1469+
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
1470+
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
1471+
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
1472+
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
1473+
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
1474+
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
1475+
0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
1476+
0x00, 0xff, 0x00, 0xff, 0x55, 0xaa, 0x55, 0xaa,
1477+
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
1478+
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
1479+
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
1480+
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
1481+
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
1482+
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
1483+
0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
1484+
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
1485+
0x01, 0xfe, 0x01, 0xfe, 0xcc, 0xcc, 0xcc, 0xff,
1486+
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
1487+
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
1488+
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
1489+
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
1490+
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
1491+
1492+
};
1493+
1494+
/* Initialization for single block read/write operation for tuning */
1495+
static void hs400_prepare_mrq(const struct cvm_mmc_slot *slot,
1496+
struct mmc_request *mrq, struct mmc_command *cmd,
1497+
struct mmc_data *data, struct scatterlist *sg,
1498+
const void *dat_buf, u32 size, bool write)
1499+
{
1500+
struct mmc_host *mmc = slot->mmc;
1501+
1502+
memset(data, 0, sizeof(*data));
1503+
memset(cmd, 0, sizeof(*cmd));
1504+
memset(mrq, 0, sizeof(*mrq));
1505+
1506+
mrq->cmd = cmd;
1507+
mrq->data = data;
1508+
cmd->opcode = write ? MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
1509+
cmd->arg = slot->hs400_tuning_block;
1510+
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1511+
data->blksz = size;
1512+
data->blocks = 1;
1513+
data->sg = sg;
1514+
data->sg_len = 1;
1515+
data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
1516+
init_completion(&(mrq->completion));
1517+
if (mmc->card)
1518+
mmc_set_data_timeout(data, mmc->card);
1519+
else
1520+
data->timeout_ns = (write ? 80 : 10) * NSEC_PER_MSEC;
1521+
sg_init_one(sg, dat_buf, size);
1522+
}
1523+
1524+
static int access_hs400_tuning_block(struct cvm_mmc_slot *slot, bool write)
1525+
{
1526+
struct mmc_request mrq = {};
1527+
struct mmc_command cmd = {};
1528+
struct mmc_data data = {};
1529+
struct scatterlist sg;
1530+
struct mmc_host *mmc = slot->mmc;
1531+
const int size = mmc->max_blk_size;
1532+
u8 *data_buf;
1533+
1534+
data_buf = kzalloc(size, GFP_KERNEL);
1535+
if (!data_buf)
1536+
return -ENOMEM;
1537+
if (write)
1538+
memcpy(data_buf, octeontx_hs400_tuning_block, size);
1539+
1540+
hs400_prepare_mrq(slot, &mrq, &cmd, &data, &sg, data_buf, size, write);
1541+
1542+
mmc_wait_for_req(mmc, &mrq);
1543+
1544+
if (!write) {
1545+
if (memcmp(data_buf, octeontx_hs400_tuning_block,
1546+
sizeof(octeontx_hs400_tuning_block))) {
1547+
kfree(data_buf);
1548+
return -EILSEQ;
1549+
}
1550+
}
1551+
kfree(data_buf);
1552+
1553+
if (cmd.error || data.error)
1554+
dev_dbg(slot->host->dev, "%s op failed, cmd: %d, data: %d\n",
1555+
write ? "write" : "read", cmd.error, data.error);
1556+
return (cmd.error || data.error) ? -ENODATA : 0;
1557+
}
1558+
1559+
/* Check for and write if necessary the tuning block for HS4000 tuning */
1560+
static int check_and_write_hs400_tuning_block(struct cvm_mmc_slot *slot)
1561+
{
1562+
int err;
1563+
1564+
if (slot->hs400_tuning_block == -1 ||
1565+
slot->hs400_tuning_block_present)
1566+
return 0;
1567+
1568+
/* Read the tuning block first and see if it's already set */
1569+
err = access_hs400_tuning_block(slot, false);
1570+
if (err == -ENODATA) {
1571+
dev_warn(slot->host->dev,
1572+
"Could not access HS400 tuning block %d in HS200 mode, err: %d\n",
1573+
slot->hs400_tuning_block, err);
1574+
return err;
1575+
} else if (!err) {
1576+
/* Everything is good, data matches, we're done */
1577+
goto done;
1578+
}
1579+
1580+
/* Attempt to write the tuning block */
1581+
err = access_hs400_tuning_block(slot, true);
1582+
if (err) {
1583+
dev_warn(slot->host->dev,
1584+
"err: %d, Could not write HS400 tuning block in HS200 mode\n",
1585+
err);
1586+
goto done;
1587+
}
1588+
1589+
/* Read after write, this should pass */
1590+
err = access_hs400_tuning_block(slot, false);
1591+
if (err)
1592+
dev_warn(slot->host->dev,
1593+
"Could not read HS400 tuning block after write, err: %d\n",
1594+
err);
1595+
1596+
done:
1597+
/* Disable HS400 tuning if we can't access the tuning block */
1598+
if (err)
1599+
slot->hs400_tuning_block = -1;
1600+
1601+
slot->hs400_tuning_block_present = !err;
1602+
1603+
return err;
1604+
}
1605+
1606+
static int tune_hs400(struct cvm_mmc_slot *slot)
1607+
{
1608+
int err = 0, start_run = -1, best_run = 0, best_start = -1;
1609+
int last_good = -1;
1610+
bool prev_ok = false;
1611+
u64 timing;
1612+
int tap;
1613+
const int size = sizeof(octeontx_hs400_tuning_block);
1614+
struct mmc_host *mmc = slot->mmc;
1615+
struct cvm_mmc_host *host = slot->host;
1616+
struct mmc_request mrq;
1617+
struct mmc_command cmd;
1618+
struct mmc_data data;
1619+
struct scatterlist sg;
1620+
u8 *data_buf;
1621+
char how[MAX_NO_OF_TAPS+1] = "";
1622+
1623+
if (slot->hs400_tuning_block == -1)
1624+
return 0;
1625+
1626+
/*
1627+
* Unfortunately, in their infinite wisdom, the eMMC standard does
1628+
* not allow for tuning in HS400 mode. The problem is that what
1629+
* makes a good tuning point for HS200 often does not work in HS400
1630+
* mode. In order to tune HS400 mode, a block (usually block 1) is
1631+
* set aside for tuning. U-Boot is responsible for writing a data
1632+
* pattern designed to generate a worst case signal. Most of this
1633+
* pattern is based off of the HS200 pattern.
1634+
*
1635+
* Each data in tap is tested by a read of this block and the center
1636+
* tap of the longest run of good reads is chosen. This code is
1637+
* largely similar to adjust_tuning() above.
1638+
*/
1639+
data_buf = kmalloc(size, GFP_KERNEL);
1640+
if (!data_buf)
1641+
return -ENOMEM;
1642+
1643+
hs400_prepare_mrq(slot, &mrq, &cmd, &data, &sg, data_buf, size, false);
1644+
1645+
/* loop over range+1 to simplify processing */
1646+
for (tap = 0; tap <= MAX_NO_OF_TAPS; tap++, prev_ok = !err) {
1647+
if (tap < MAX_NO_OF_TAPS) {
1648+
cvm_mmc_clk_config(host, CLK_OFF);
1649+
timing = readq(host->base + MIO_EMM_TIMING(host));
1650+
timing = FIELD_PREP(MIO_EMM_TIMING_DATA_IN, tap);
1651+
writeq(timing, host->base + MIO_EMM_TIMING(host));
1652+
cvm_mmc_clk_config(host, CLK_ON);
1653+
1654+
dev_dbg(host->dev, "HS400 testing data in tap %d\n",
1655+
tap);
1656+
mmc_wait_for_req(mmc, &mrq);
1657+
if (cmd.error | data.error) {
1658+
err = cmd.error ? cmd.error : data.error;
1659+
how[tap] = '-';
1660+
dev_dbg(host->dev,
1661+
"HS400 tuning cmd err: %d, data error: %d\n",
1662+
cmd.error, data.error);
1663+
} else { /* Validate data */
1664+
err = memcmp(data_buf,
1665+
octeontx_hs400_tuning_block, size);
1666+
1667+
how[tap] = "d+"[!err];
1668+
dev_dbg(host->dev,
1669+
"HS400 read OK at tap %d, data %s\n",
1670+
tap, err ? "mismatch" : "ok");
1671+
}
1672+
1673+
if (!err)
1674+
last_good = tap;
1675+
} else {
1676+
/*
1677+
* putting the end+1 case in loop simplifies
1678+
* logic, allowing 'prev_ok' to process a
1679+
* sweet spot in tuning which extends to wall.
1680+
*/
1681+
err = -EILSEQ;
1682+
}
1683+
1684+
if (!err) {
1685+
/*
1686+
* If no CRC/etc errors in response, but previous
1687+
* failed, note the start of a new run
1688+
*/
1689+
if (!prev_ok)
1690+
start_run = tap;
1691+
} else if (prev_ok) {
1692+
int run = tap - 1 - start_run;
1693+
1694+
/* did we just exit a wider sweet spot? */
1695+
if (start_run >= 0 && run > best_run) {
1696+
best_start = start_run;
1697+
best_run = run;
1698+
}
1699+
}
1700+
}
1701+
1702+
kfree(data_buf);
1703+
if (best_start < 0) {
1704+
dev_warn(host->dev, "%s %lldMHz tuning HS400 data in failed\n",
1705+
mmc_hostname(mmc), slot->clock / 1000000);
1706+
return -EINVAL;
1707+
}
1708+
1709+
tap = best_start + best_run / 2;
1710+
how[tap] = '@';
1711+
if (tapdance) {
1712+
tap = last_good - tapdance;
1713+
how[tap] = 'X';
1714+
}
1715+
dev_dbg(host->dev, "%s/HS400 data in %d/%d/%d %s\n",
1716+
mmc_hostname(mmc), best_start, tap,
1717+
best_start + best_run, how);
1718+
slot->taps &= ~MIO_EMM_TIMING_DATA_IN;
1719+
slot->taps |= FIELD_PREP(MIO_EMM_TIMING_DATA_IN, tap);
1720+
dev_dbg(host->dev, "HS400 data input tap: %d\n", tap);
1721+
dev_dbg(host->dev, "%s\n", how);
1722+
cvm_mmc_set_timing(slot);
1723+
1724+
return 0;
1725+
}
1726+
14261727
static u32 max_supported_frequency(struct cvm_mmc_host *host)
14271728
{
14281729
/* Default maximum freqeuncey is 52000000 for chip prior to 9X */
@@ -1572,6 +1873,10 @@ static void cvm_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
15721873
cvm_mmc_configure_delay(slot);
15731874
out:
15741875
host->release_bus(host);
1876+
if (ios->timing == MMC_TIMING_MMC_HS)
1877+
check_and_write_hs400_tuning_block(slot);
1878+
else if (ios->timing == MMC_TIMING_MMC_HS400)
1879+
tune_hs400(slot);
15751880
}
15761881

15771882
static struct adj adj[] = {
@@ -1600,6 +1905,8 @@ static int cvm_scan_tuning(struct mmc_host *mmc, u32 opcode)
16001905
}
16011906

16021907
cvm_mmc_set_timing(slot);
1908+
if (!slot->hs400_tuning_block_present)
1909+
check_and_write_hs400_tuning_block(slot);
16031910
return 0;
16041911
}
16051912

@@ -1768,6 +2075,9 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot)
17682075
if (ret)
17692076
return ret;
17702077

2078+
slot->hs400_tuning_block = -1U;
2079+
of_property_read_u32(node, "marvell,hs400-tuning-block",
2080+
&slot->hs400_tuning_block);
17712081
/* Set bus width from obsolete properties, if unset */
17722082
if (!(mmc->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA))) {
17732083
of_property_read_u32(node, "cavium,bus-max-width", &bus_width);

drivers/mmc/host/cavium.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ struct cvm_mmc_slot {
168168
int bus_id;
169169
bool cmd6_pending;
170170
u64 want_switch;
171+
u32 hs400_tuning_block; /* Block number used for tuning */
172+
bool hs400_tuning_block_present;
171173
};
172174

173175
struct cvm_mmc_cr_type {

0 commit comments

Comments
 (0)