hwrng: cn10k - Add extended trng register support

The way random data is read from hardware has changed from
Octeon CN10KA-B0 and later SoCs onwards. A new set of registers
have been added to read random data and to verify whether the
read data is valid or not. This patch extends and uses
RNM_PF_TRNG_DAT and RNM_PF_TRNG_STS CSRs to read random number
and status for the applicable silicon variants.

Signed-off-by: Bharat Bhushan <bbhushan2@marvell.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Bharat Bhushan 2023-05-25 09:12:00 +05:30 committed by Herbert Xu
parent efbc7764c4
commit 506579e88c
1 changed files with 59 additions and 4 deletions

View File

@ -23,14 +23,49 @@
#define RNM_PF_RANDOM 0x400 #define RNM_PF_RANDOM 0x400
#define RNM_TRNG_RESULT 0x408 #define RNM_TRNG_RESULT 0x408
/* Extended TRNG Read and Status Registers */
#define RNM_PF_TRNG_DAT 0x1000
#define RNM_PF_TRNG_RES 0x1008
struct cn10k_rng { struct cn10k_rng {
void __iomem *reg_base; void __iomem *reg_base;
struct hwrng ops; struct hwrng ops;
struct pci_dev *pdev; struct pci_dev *pdev;
/* Octeon CN10K-A A0/A1, CNF10K-A A0/A1 and CNF10K-B A0/B0
* does not support extended TRNG registers
*/
bool extended_trng_regs;
}; };
#define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f
#define PCI_SUBSYS_DEVID_CN10K_A_RNG 0xB900
#define PCI_SUBSYS_DEVID_CNF10K_A_RNG 0xBA00
#define PCI_SUBSYS_DEVID_CNF10K_B_RNG 0xBC00
static bool cn10k_is_extended_trng_regs_supported(struct pci_dev *pdev)
{
/* CN10K-A A0/A1 */
if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_RNG) &&
(!pdev->revision || (pdev->revision & 0xff) == 0x50 ||
(pdev->revision & 0xff) == 0x51))
return false;
/* CNF10K-A A0 */
if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_RNG) &&
(!pdev->revision || (pdev->revision & 0xff) == 0x60 ||
(pdev->revision & 0xff) == 0x61))
return false;
/* CNF10K-B A0/B0 */
if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_B_RNG) &&
(!pdev->revision || (pdev->revision & 0xff) == 0x70 ||
(pdev->revision & 0xff) == 0x74))
return false;
return true;
}
static unsigned long reset_rng_health_state(struct cn10k_rng *rng) static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
{ {
struct arm_smccc_res res; struct arm_smccc_res res;
@ -63,9 +98,23 @@ static int check_rng_health(struct cn10k_rng *rng)
return 0; return 0;
} }
static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value) /* Returns true when valid data available otherwise return false */
static bool cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
{ {
u16 retry_count = 0;
u64 upper, lower; u64 upper, lower;
u64 status;
if (rng->extended_trng_regs) {
do {
*value = readq(rng->reg_base + RNM_PF_TRNG_DAT);
if (*value)
return true;
status = readq(rng->reg_base + RNM_PF_TRNG_RES);
if (!status && (retry_count++ > 0x1000))
return false;
} while (!status);
}
*value = readq(rng->reg_base + RNM_PF_RANDOM); *value = readq(rng->reg_base + RNM_PF_RANDOM);
@ -82,6 +131,7 @@ static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
*value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF); *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
} }
return true;
} }
static int cn10k_rng_read(struct hwrng *hwrng, void *data, static int cn10k_rng_read(struct hwrng *hwrng, void *data,
@ -100,7 +150,8 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
size = max; size = max;
while (size >= 8) { while (size >= 8) {
cn10k_read_trng(rng, &value); if (!cn10k_read_trng(rng, &value))
goto out;
*((u64 *)pos) = value; *((u64 *)pos) = value;
size -= 8; size -= 8;
@ -108,7 +159,8 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
} }
if (size > 0) { if (size > 0) {
cn10k_read_trng(rng, &value); if (!cn10k_read_trng(rng, &value))
goto out;
while (size > 0) { while (size > 0) {
*pos = (u8)value; *pos = (u8)value;
@ -118,6 +170,7 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
} }
} }
out:
return max - size; return max - size;
} }
@ -144,9 +197,11 @@ static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (!rng->ops.name) if (!rng->ops.name)
return -ENOMEM; return -ENOMEM;
rng->ops.read = cn10k_rng_read; rng->ops.read = cn10k_rng_read;
rng->ops.priv = (unsigned long)rng; rng->ops.priv = (unsigned long)rng;
rng->extended_trng_regs = cn10k_is_extended_trng_regs_supported(pdev);
reset_rng_health_state(rng); reset_rng_health_state(rng);
err = devm_hwrng_register(&pdev->dev, &rng->ops); err = devm_hwrng_register(&pdev->dev, &rng->ops);