10289e9beSOlivier Sobrie // SPDX-License-Identifier: GPL-2.0 20289e9beSOlivier Sobrie // Copyright (c) 2020 Silex Insight 30289e9beSOlivier Sobrie 40289e9beSOlivier Sobrie #include <linux/delay.h> 50289e9beSOlivier Sobrie #include <linux/hw_random.h> 60289e9beSOlivier Sobrie #include <linux/io.h> 70289e9beSOlivier Sobrie #include <linux/iopoll.h> 8271dead3SHerbert Xu #include <linux/kernel.h> 90289e9beSOlivier Sobrie #include <linux/mod_devicetable.h> 100289e9beSOlivier Sobrie #include <linux/module.h> 110289e9beSOlivier Sobrie #include <linux/platform_device.h> 120289e9beSOlivier Sobrie #include <linux/workqueue.h> 130289e9beSOlivier Sobrie 140289e9beSOlivier Sobrie #define BA431_RESET_DELAY 1 /* usec */ 150289e9beSOlivier Sobrie #define BA431_RESET_READ_STATUS_TIMEOUT 1000 /* usec */ 160289e9beSOlivier Sobrie #define BA431_RESET_READ_STATUS_INTERVAL 10 /* usec */ 170289e9beSOlivier Sobrie #define BA431_READ_RETRY_INTERVAL 1 /* usec */ 180289e9beSOlivier Sobrie 190289e9beSOlivier Sobrie #define BA431_REG_CTRL 0x00 200289e9beSOlivier Sobrie #define BA431_REG_FIFO_LEVEL 0x04 210289e9beSOlivier Sobrie #define BA431_REG_STATUS 0x30 220289e9beSOlivier Sobrie #define BA431_REG_FIFODATA 0x80 230289e9beSOlivier Sobrie 240289e9beSOlivier Sobrie #define BA431_CTRL_ENABLE BIT(0) 250289e9beSOlivier Sobrie #define BA431_CTRL_SOFTRESET BIT(8) 260289e9beSOlivier Sobrie 270289e9beSOlivier Sobrie #define BA431_STATUS_STATE_MASK (BIT(1) | BIT(2) | BIT(3)) 280289e9beSOlivier Sobrie #define BA431_STATUS_STATE_OFFSET 1 290289e9beSOlivier Sobrie 300289e9beSOlivier Sobrie enum ba431_state { 310289e9beSOlivier Sobrie BA431_STATE_RESET, 320289e9beSOlivier Sobrie BA431_STATE_STARTUP, 330289e9beSOlivier Sobrie BA431_STATE_FIFOFULLON, 340289e9beSOlivier Sobrie BA431_STATE_FIFOFULLOFF, 350289e9beSOlivier Sobrie BA431_STATE_RUNNING, 360289e9beSOlivier Sobrie BA431_STATE_ERROR 370289e9beSOlivier Sobrie }; 380289e9beSOlivier Sobrie 390289e9beSOlivier Sobrie struct ba431_trng { 400289e9beSOlivier Sobrie struct device *dev; 410289e9beSOlivier Sobrie void __iomem *base; 420289e9beSOlivier Sobrie struct hwrng rng; 430289e9beSOlivier Sobrie atomic_t reset_pending; 440289e9beSOlivier Sobrie struct work_struct reset_work; 450289e9beSOlivier Sobrie }; 460289e9beSOlivier Sobrie 470289e9beSOlivier Sobrie static inline u32 ba431_trng_read_reg(struct ba431_trng *ba431, u32 reg) 480289e9beSOlivier Sobrie { 490289e9beSOlivier Sobrie return ioread32(ba431->base + reg); 500289e9beSOlivier Sobrie } 510289e9beSOlivier Sobrie 520289e9beSOlivier Sobrie static inline void ba431_trng_write_reg(struct ba431_trng *ba431, u32 reg, 530289e9beSOlivier Sobrie u32 val) 540289e9beSOlivier Sobrie { 550289e9beSOlivier Sobrie iowrite32(val, ba431->base + reg); 560289e9beSOlivier Sobrie } 570289e9beSOlivier Sobrie 580289e9beSOlivier Sobrie static inline enum ba431_state ba431_trng_get_state(struct ba431_trng *ba431) 590289e9beSOlivier Sobrie { 600289e9beSOlivier Sobrie u32 status = ba431_trng_read_reg(ba431, BA431_REG_STATUS); 610289e9beSOlivier Sobrie 620289e9beSOlivier Sobrie return (status & BA431_STATUS_STATE_MASK) >> BA431_STATUS_STATE_OFFSET; 630289e9beSOlivier Sobrie } 640289e9beSOlivier Sobrie 650289e9beSOlivier Sobrie static int ba431_trng_is_in_error(struct ba431_trng *ba431) 660289e9beSOlivier Sobrie { 670289e9beSOlivier Sobrie enum ba431_state state = ba431_trng_get_state(ba431); 680289e9beSOlivier Sobrie 690289e9beSOlivier Sobrie if ((state < BA431_STATE_STARTUP) || 700289e9beSOlivier Sobrie (state >= BA431_STATE_ERROR)) 710289e9beSOlivier Sobrie return 1; 720289e9beSOlivier Sobrie 730289e9beSOlivier Sobrie return 0; 740289e9beSOlivier Sobrie } 750289e9beSOlivier Sobrie 760289e9beSOlivier Sobrie static int ba431_trng_reset(struct ba431_trng *ba431) 770289e9beSOlivier Sobrie { 780289e9beSOlivier Sobrie int ret; 790289e9beSOlivier Sobrie 800289e9beSOlivier Sobrie /* Disable interrupts, random generation and enable the softreset */ 810289e9beSOlivier Sobrie ba431_trng_write_reg(ba431, BA431_REG_CTRL, BA431_CTRL_SOFTRESET); 820289e9beSOlivier Sobrie udelay(BA431_RESET_DELAY); 830289e9beSOlivier Sobrie ba431_trng_write_reg(ba431, BA431_REG_CTRL, BA431_CTRL_ENABLE); 840289e9beSOlivier Sobrie 850289e9beSOlivier Sobrie /* Wait until the state changed */ 860289e9beSOlivier Sobrie if (readx_poll_timeout(ba431_trng_is_in_error, ba431, ret, !ret, 870289e9beSOlivier Sobrie BA431_RESET_READ_STATUS_INTERVAL, 880289e9beSOlivier Sobrie BA431_RESET_READ_STATUS_TIMEOUT)) { 890289e9beSOlivier Sobrie dev_err(ba431->dev, "reset failed (state: %d)\n", 900289e9beSOlivier Sobrie ba431_trng_get_state(ba431)); 910289e9beSOlivier Sobrie return -ETIMEDOUT; 920289e9beSOlivier Sobrie } 930289e9beSOlivier Sobrie 940289e9beSOlivier Sobrie dev_info(ba431->dev, "reset done\n"); 950289e9beSOlivier Sobrie 960289e9beSOlivier Sobrie return 0; 970289e9beSOlivier Sobrie } 980289e9beSOlivier Sobrie 990289e9beSOlivier Sobrie static void ba431_trng_reset_work(struct work_struct *work) 1000289e9beSOlivier Sobrie { 1010289e9beSOlivier Sobrie struct ba431_trng *ba431 = container_of(work, struct ba431_trng, 1020289e9beSOlivier Sobrie reset_work); 1030289e9beSOlivier Sobrie ba431_trng_reset(ba431); 1040289e9beSOlivier Sobrie atomic_set(&ba431->reset_pending, 0); 1050289e9beSOlivier Sobrie } 1060289e9beSOlivier Sobrie 1070289e9beSOlivier Sobrie static void ba431_trng_schedule_reset(struct ba431_trng *ba431) 1080289e9beSOlivier Sobrie { 1090289e9beSOlivier Sobrie if (atomic_cmpxchg(&ba431->reset_pending, 0, 1)) 1100289e9beSOlivier Sobrie return; 1110289e9beSOlivier Sobrie 1120289e9beSOlivier Sobrie schedule_work(&ba431->reset_work); 1130289e9beSOlivier Sobrie } 1140289e9beSOlivier Sobrie 1150289e9beSOlivier Sobrie static int ba431_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 1160289e9beSOlivier Sobrie { 1170289e9beSOlivier Sobrie struct ba431_trng *ba431 = container_of(rng, struct ba431_trng, rng); 1180289e9beSOlivier Sobrie u32 *data = buf; 1190289e9beSOlivier Sobrie unsigned int level, i; 1200289e9beSOlivier Sobrie int n = 0; 1210289e9beSOlivier Sobrie 1220289e9beSOlivier Sobrie while (max > 0) { 1230289e9beSOlivier Sobrie level = ba431_trng_read_reg(ba431, BA431_REG_FIFO_LEVEL); 1240289e9beSOlivier Sobrie if (!level) { 1250289e9beSOlivier Sobrie if (ba431_trng_is_in_error(ba431)) { 1260289e9beSOlivier Sobrie ba431_trng_schedule_reset(ba431); 1270289e9beSOlivier Sobrie break; 1280289e9beSOlivier Sobrie } 1290289e9beSOlivier Sobrie 1300289e9beSOlivier Sobrie if (!wait) 1310289e9beSOlivier Sobrie break; 1320289e9beSOlivier Sobrie 1330289e9beSOlivier Sobrie udelay(BA431_READ_RETRY_INTERVAL); 1340289e9beSOlivier Sobrie continue; 1350289e9beSOlivier Sobrie } 1360289e9beSOlivier Sobrie 1370289e9beSOlivier Sobrie i = level; 1380289e9beSOlivier Sobrie do { 1390289e9beSOlivier Sobrie data[n++] = ba431_trng_read_reg(ba431, 1400289e9beSOlivier Sobrie BA431_REG_FIFODATA); 1410289e9beSOlivier Sobrie max -= sizeof(*data); 1420289e9beSOlivier Sobrie } while (--i && (max > 0)); 1430289e9beSOlivier Sobrie 1440289e9beSOlivier Sobrie if (ba431_trng_is_in_error(ba431)) { 1450289e9beSOlivier Sobrie n -= (level - i); 1460289e9beSOlivier Sobrie ba431_trng_schedule_reset(ba431); 1470289e9beSOlivier Sobrie break; 1480289e9beSOlivier Sobrie } 1490289e9beSOlivier Sobrie } 1500289e9beSOlivier Sobrie 1510289e9beSOlivier Sobrie n *= sizeof(data); 1520289e9beSOlivier Sobrie return (n || !wait) ? n : -EIO; 1530289e9beSOlivier Sobrie } 1540289e9beSOlivier Sobrie 1550289e9beSOlivier Sobrie static void ba431_trng_cleanup(struct hwrng *rng) 1560289e9beSOlivier Sobrie { 1570289e9beSOlivier Sobrie struct ba431_trng *ba431 = container_of(rng, struct ba431_trng, rng); 1580289e9beSOlivier Sobrie 1590289e9beSOlivier Sobrie ba431_trng_write_reg(ba431, BA431_REG_CTRL, 0); 1600289e9beSOlivier Sobrie cancel_work_sync(&ba431->reset_work); 1610289e9beSOlivier Sobrie } 1620289e9beSOlivier Sobrie 1630289e9beSOlivier Sobrie static int ba431_trng_init(struct hwrng *rng) 1640289e9beSOlivier Sobrie { 1650289e9beSOlivier Sobrie struct ba431_trng *ba431 = container_of(rng, struct ba431_trng, rng); 1660289e9beSOlivier Sobrie 1670289e9beSOlivier Sobrie return ba431_trng_reset(ba431); 1680289e9beSOlivier Sobrie } 1690289e9beSOlivier Sobrie 1700289e9beSOlivier Sobrie static int ba431_trng_probe(struct platform_device *pdev) 1710289e9beSOlivier Sobrie { 1720289e9beSOlivier Sobrie struct ba431_trng *ba431; 1730289e9beSOlivier Sobrie struct resource *res; 1740289e9beSOlivier Sobrie int ret; 1750289e9beSOlivier Sobrie 1760289e9beSOlivier Sobrie ba431 = devm_kzalloc(&pdev->dev, sizeof(*ba431), GFP_KERNEL); 1770289e9beSOlivier Sobrie if (!ba431) 1780289e9beSOlivier Sobrie return -ENOMEM; 1790289e9beSOlivier Sobrie 1800289e9beSOlivier Sobrie ba431->dev = &pdev->dev; 1810289e9beSOlivier Sobrie 1820289e9beSOlivier Sobrie res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1830289e9beSOlivier Sobrie ba431->base = devm_ioremap_resource(&pdev->dev, res); 1840289e9beSOlivier Sobrie if (IS_ERR(ba431->base)) 1850289e9beSOlivier Sobrie return PTR_ERR(ba431->base); 1860289e9beSOlivier Sobrie 1870289e9beSOlivier Sobrie atomic_set(&ba431->reset_pending, 0); 1880289e9beSOlivier Sobrie INIT_WORK(&ba431->reset_work, ba431_trng_reset_work); 1890289e9beSOlivier Sobrie ba431->rng.name = pdev->name; 1900289e9beSOlivier Sobrie ba431->rng.init = ba431_trng_init; 1910289e9beSOlivier Sobrie ba431->rng.cleanup = ba431_trng_cleanup; 1920289e9beSOlivier Sobrie ba431->rng.read = ba431_trng_read; 1930289e9beSOlivier Sobrie 1940289e9beSOlivier Sobrie platform_set_drvdata(pdev, ba431); 1950289e9beSOlivier Sobrie 196*3e2ccc74STian Tao ret = devm_hwrng_register(&pdev->dev, &ba431->rng); 1970289e9beSOlivier Sobrie if (ret) { 1980289e9beSOlivier Sobrie dev_err(&pdev->dev, "BA431 registration failed (%d)\n", ret); 1990289e9beSOlivier Sobrie return ret; 2000289e9beSOlivier Sobrie } 2010289e9beSOlivier Sobrie 2020289e9beSOlivier Sobrie dev_info(&pdev->dev, "BA431 TRNG registered\n"); 2030289e9beSOlivier Sobrie 2040289e9beSOlivier Sobrie return 0; 2050289e9beSOlivier Sobrie } 2060289e9beSOlivier Sobrie 2070289e9beSOlivier Sobrie static const struct of_device_id ba431_trng_dt_ids[] = { 2080289e9beSOlivier Sobrie { .compatible = "silex-insight,ba431-rng", .data = NULL }, 2090289e9beSOlivier Sobrie { /* sentinel */ } 2100289e9beSOlivier Sobrie }; 2110289e9beSOlivier Sobrie MODULE_DEVICE_TABLE(of, ba431_trng_dt_ids); 2120289e9beSOlivier Sobrie 2130289e9beSOlivier Sobrie static struct platform_driver ba431_trng_driver = { 2140289e9beSOlivier Sobrie .driver = { 2150289e9beSOlivier Sobrie .name = "ba431-rng", 2160289e9beSOlivier Sobrie .of_match_table = ba431_trng_dt_ids, 2170289e9beSOlivier Sobrie }, 2180289e9beSOlivier Sobrie .probe = ba431_trng_probe, 2190289e9beSOlivier Sobrie }; 2200289e9beSOlivier Sobrie 2210289e9beSOlivier Sobrie module_platform_driver(ba431_trng_driver); 2220289e9beSOlivier Sobrie 2230289e9beSOlivier Sobrie MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); 2240289e9beSOlivier Sobrie MODULE_DESCRIPTION("TRNG driver for Silex Insight BA431"); 2250289e9beSOlivier Sobrie MODULE_LICENSE("GPL"); 226