1*6727c444SEric Biggers // SPDX-License-Identifier: GPL-2.0 2*6727c444SEric Biggers // Copyright (c) 2017-18 Linaro Limited 3*6727c444SEric Biggers // 4*6727c444SEric Biggers // Based on msm-rng.c and downstream driver 5*6727c444SEric Biggers 6*6727c444SEric Biggers #include <linux/acpi.h> 7*6727c444SEric Biggers #include <linux/clk.h> 8*6727c444SEric Biggers #include <linux/hw_random.h> 9*6727c444SEric Biggers #include <linux/io.h> 10*6727c444SEric Biggers #include <linux/iopoll.h> 11*6727c444SEric Biggers #include <linux/kernel.h> 12*6727c444SEric Biggers #include <linux/module.h> 13*6727c444SEric Biggers #include <linux/of.h> 14*6727c444SEric Biggers #include <linux/platform_device.h> 15*6727c444SEric Biggers 16*6727c444SEric Biggers /* Device specific register offsets */ 17*6727c444SEric Biggers #define PRNG_DATA_OUT 0x0000 18*6727c444SEric Biggers #define PRNG_STATUS 0x0004 19*6727c444SEric Biggers #define PRNG_LFSR_CFG 0x0100 20*6727c444SEric Biggers #define PRNG_CONFIG 0x0104 21*6727c444SEric Biggers 22*6727c444SEric Biggers /* Device specific register masks and config values */ 23*6727c444SEric Biggers #define PRNG_LFSR_CFG_MASK 0x0000ffff 24*6727c444SEric Biggers #define PRNG_LFSR_CFG_CLOCKS 0x0000dddd 25*6727c444SEric Biggers #define PRNG_CONFIG_HW_ENABLE BIT(1) 26*6727c444SEric Biggers #define PRNG_STATUS_DATA_AVAIL BIT(0) 27*6727c444SEric Biggers 28*6727c444SEric Biggers #define WORD_SZ 4 29*6727c444SEric Biggers 30*6727c444SEric Biggers #define QCOM_TRNG_QUALITY 1024 31*6727c444SEric Biggers 32*6727c444SEric Biggers struct qcom_rng { 33*6727c444SEric Biggers void __iomem *base; 34*6727c444SEric Biggers struct clk *clk; 35*6727c444SEric Biggers struct hwrng hwrng; 36*6727c444SEric Biggers }; 37*6727c444SEric Biggers 38*6727c444SEric Biggers struct qcom_rng_match_data { 39*6727c444SEric Biggers bool hwrng_support; 40*6727c444SEric Biggers }; 41*6727c444SEric Biggers 42*6727c444SEric Biggers static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) 43*6727c444SEric Biggers { 44*6727c444SEric Biggers unsigned int currsize = 0; 45*6727c444SEric Biggers u32 val; 46*6727c444SEric Biggers int ret; 47*6727c444SEric Biggers 48*6727c444SEric Biggers /* read random data from hardware */ 49*6727c444SEric Biggers do { 50*6727c444SEric Biggers ret = readl_poll_timeout(rng->base + PRNG_STATUS, val, 51*6727c444SEric Biggers val & PRNG_STATUS_DATA_AVAIL, 52*6727c444SEric Biggers 200, 10000); 53*6727c444SEric Biggers if (ret) 54*6727c444SEric Biggers return ret; 55*6727c444SEric Biggers 56*6727c444SEric Biggers val = readl_relaxed(rng->base + PRNG_DATA_OUT); 57*6727c444SEric Biggers 58*6727c444SEric Biggers if ((max - currsize) >= WORD_SZ) { 59*6727c444SEric Biggers memcpy(data, &val, WORD_SZ); 60*6727c444SEric Biggers data += WORD_SZ; 61*6727c444SEric Biggers currsize += WORD_SZ; 62*6727c444SEric Biggers } else { 63*6727c444SEric Biggers /* copy only remaining bytes */ 64*6727c444SEric Biggers memcpy(data, &val, max - currsize); 65*6727c444SEric Biggers currsize = max; 66*6727c444SEric Biggers } 67*6727c444SEric Biggers } while (currsize < max); 68*6727c444SEric Biggers 69*6727c444SEric Biggers return currsize; 70*6727c444SEric Biggers } 71*6727c444SEric Biggers 72*6727c444SEric Biggers static int qcom_hwrng_init(struct hwrng *hwrng) 73*6727c444SEric Biggers { 74*6727c444SEric Biggers struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 75*6727c444SEric Biggers 76*6727c444SEric Biggers return clk_prepare_enable(qrng->clk); 77*6727c444SEric Biggers } 78*6727c444SEric Biggers 79*6727c444SEric Biggers static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) 80*6727c444SEric Biggers { 81*6727c444SEric Biggers struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 82*6727c444SEric Biggers 83*6727c444SEric Biggers return qcom_rng_read(qrng, data, max); 84*6727c444SEric Biggers } 85*6727c444SEric Biggers 86*6727c444SEric Biggers static void qcom_hwrng_cleanup(struct hwrng *hwrng) 87*6727c444SEric Biggers { 88*6727c444SEric Biggers struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); 89*6727c444SEric Biggers 90*6727c444SEric Biggers clk_disable_unprepare(qrng->clk); 91*6727c444SEric Biggers } 92*6727c444SEric Biggers 93*6727c444SEric Biggers static int qcom_rng_probe(struct platform_device *pdev) 94*6727c444SEric Biggers { 95*6727c444SEric Biggers const struct qcom_rng_match_data *match_data; 96*6727c444SEric Biggers struct qcom_rng *rng; 97*6727c444SEric Biggers int ret; 98*6727c444SEric Biggers 99*6727c444SEric Biggers match_data = device_get_match_data(&pdev->dev); 100*6727c444SEric Biggers if (match_data == NULL || !match_data->hwrng_support) { 101*6727c444SEric Biggers dev_info(&pdev->dev, "TRNG support not detected\n"); 102*6727c444SEric Biggers /* 103*6727c444SEric Biggers * In this case the driver does nothing except the dev_info(), 104*6727c444SEric Biggers * but bind the device anyway to avoid effects on GCC state. 105*6727c444SEric Biggers */ 106*6727c444SEric Biggers return 0; 107*6727c444SEric Biggers } 108*6727c444SEric Biggers 109*6727c444SEric Biggers rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 110*6727c444SEric Biggers if (!rng) 111*6727c444SEric Biggers return -ENOMEM; 112*6727c444SEric Biggers 113*6727c444SEric Biggers rng->base = devm_platform_ioremap_resource(pdev, 0); 114*6727c444SEric Biggers if (IS_ERR(rng->base)) 115*6727c444SEric Biggers return PTR_ERR(rng->base); 116*6727c444SEric Biggers 117*6727c444SEric Biggers rng->clk = devm_clk_get_optional(&pdev->dev, "core"); 118*6727c444SEric Biggers if (IS_ERR(rng->clk)) 119*6727c444SEric Biggers return PTR_ERR(rng->clk); 120*6727c444SEric Biggers 121*6727c444SEric Biggers rng->hwrng.name = "qcom_hwrng"; 122*6727c444SEric Biggers rng->hwrng.init = qcom_hwrng_init; 123*6727c444SEric Biggers rng->hwrng.read = qcom_hwrng_read; 124*6727c444SEric Biggers rng->hwrng.cleanup = qcom_hwrng_cleanup; 125*6727c444SEric Biggers rng->hwrng.quality = QCOM_TRNG_QUALITY; 126*6727c444SEric Biggers ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); 127*6727c444SEric Biggers if (ret) 128*6727c444SEric Biggers dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret); 129*6727c444SEric Biggers return ret; 130*6727c444SEric Biggers } 131*6727c444SEric Biggers 132*6727c444SEric Biggers static struct qcom_rng_match_data qcom_prng_match_data = { 133*6727c444SEric Biggers .hwrng_support = false, 134*6727c444SEric Biggers }; 135*6727c444SEric Biggers 136*6727c444SEric Biggers static struct qcom_rng_match_data qcom_prng_ee_match_data = { 137*6727c444SEric Biggers .hwrng_support = false, 138*6727c444SEric Biggers }; 139*6727c444SEric Biggers 140*6727c444SEric Biggers static struct qcom_rng_match_data qcom_trng_match_data = { 141*6727c444SEric Biggers .hwrng_support = true, 142*6727c444SEric Biggers }; 143*6727c444SEric Biggers 144*6727c444SEric Biggers static const struct acpi_device_id __maybe_unused qcom_rng_acpi_match[] = { 145*6727c444SEric Biggers { .id = "QCOM8160", .driver_data = (kernel_ulong_t)&qcom_prng_ee_match_data }, 146*6727c444SEric Biggers {} 147*6727c444SEric Biggers }; 148*6727c444SEric Biggers MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match); 149*6727c444SEric Biggers 150*6727c444SEric Biggers static const struct of_device_id __maybe_unused qcom_rng_of_match[] = { 151*6727c444SEric Biggers { .compatible = "qcom,prng", .data = &qcom_prng_match_data }, 152*6727c444SEric Biggers { .compatible = "qcom,prng-ee", .data = &qcom_prng_ee_match_data }, 153*6727c444SEric Biggers { .compatible = "qcom,trng", .data = &qcom_trng_match_data }, 154*6727c444SEric Biggers {} 155*6727c444SEric Biggers }; 156*6727c444SEric Biggers MODULE_DEVICE_TABLE(of, qcom_rng_of_match); 157*6727c444SEric Biggers 158*6727c444SEric Biggers static struct platform_driver qcom_rng_driver = { 159*6727c444SEric Biggers .probe = qcom_rng_probe, 160*6727c444SEric Biggers .driver = { 161*6727c444SEric Biggers .name = KBUILD_MODNAME, 162*6727c444SEric Biggers .of_match_table = qcom_rng_of_match, 163*6727c444SEric Biggers .acpi_match_table = ACPI_PTR(qcom_rng_acpi_match), 164*6727c444SEric Biggers } 165*6727c444SEric Biggers }; 166*6727c444SEric Biggers module_platform_driver(qcom_rng_driver); 167*6727c444SEric Biggers 168*6727c444SEric Biggers MODULE_ALIAS("platform:" KBUILD_MODNAME); 169*6727c444SEric Biggers MODULE_DESCRIPTION("Qualcomm random number generator driver"); 170*6727c444SEric Biggers MODULE_LICENSE("GPL v2"); 171