1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Renesas R-Car E-FUSE/OTP Driver 4 * 5 * Copyright (C) 2024 Glider bv 6 */ 7 8 #include <linux/device.h> 9 #include <linux/export.h> 10 #include <linux/io.h> 11 #include <linux/nvmem-provider.h> 12 #include <linux/platform_device.h> 13 #include <linux/pm_runtime.h> 14 #include <linux/property.h> 15 16 struct rcar_fuse { 17 struct nvmem_keepout keepouts[2]; 18 struct nvmem_device *nvmem; 19 struct device *dev; 20 void __iomem *base; 21 }; 22 23 struct rcar_fuse_data { 24 unsigned int bank; /* 0: PFC + E-FUSE, 1: OPT_MEM + E-FUSE */ 25 unsigned int start; /* inclusive */ 26 unsigned int end; /* exclusive */ 27 }; 28 29 static int rcar_fuse_reg_read(void *priv, unsigned int offset, void *val, 30 size_t bytes) 31 { 32 struct rcar_fuse *fuse = priv; 33 int ret; 34 35 ret = pm_runtime_resume_and_get(fuse->dev); 36 if (ret < 0) 37 return ret; 38 39 __ioread32_copy(val, fuse->base + offset, bytes / 4); 40 41 pm_runtime_put(fuse->dev); 42 43 return 0; 44 } 45 46 static int rcar_fuse_probe(struct platform_device *pdev) 47 { 48 struct device *dev = &pdev->dev; 49 const struct rcar_fuse_data *data = device_get_match_data(dev); 50 struct nvmem_config config = { 51 .dev = dev, 52 .name = "rcar-fuse", 53 .id = NVMEM_DEVID_NONE, 54 .owner = THIS_MODULE, 55 .type = NVMEM_TYPE_OTP, 56 .read_only = true, 57 .root_only = true, 58 .reg_read = rcar_fuse_reg_read, 59 .word_size = 4, 60 .stride = 4, 61 }; 62 struct rcar_fuse *fuse; 63 struct resource *res; 64 int ret; 65 66 ret = devm_pm_runtime_enable(dev); 67 if (ret < 0) 68 return ret; 69 70 fuse = devm_kzalloc(dev, sizeof(*fuse), GFP_KERNEL); 71 if (!fuse) 72 return -ENOMEM; 73 74 fuse->base = devm_platform_get_and_ioremap_resource(pdev, data->bank, 75 &res); 76 if (IS_ERR(fuse->base)) 77 return PTR_ERR(fuse->base); 78 79 fuse->dev = dev; 80 fuse->keepouts[0].start = 0; 81 fuse->keepouts[0].end = data->start; 82 fuse->keepouts[1].start = data->end; 83 fuse->keepouts[1].end = resource_size(res); 84 85 config.keepout = fuse->keepouts; 86 config.nkeepout = ARRAY_SIZE(fuse->keepouts); 87 config.size = resource_size(res); 88 config.priv = fuse; 89 90 fuse->nvmem = devm_nvmem_register(dev, &config); 91 if (IS_ERR(fuse->nvmem)) 92 return dev_err_probe(dev, PTR_ERR(fuse->nvmem), 93 "Failed to register NVMEM device\n"); 94 95 return 0; 96 } 97 98 static const struct rcar_fuse_data rcar_fuse_v3u = { 99 .bank = 0, 100 .start = 0x0c0, 101 .end = 0x0e8, 102 }; 103 104 static const struct rcar_fuse_data rcar_fuse_s4 = { 105 .bank = 0, 106 .start = 0x0c0, 107 .end = 0x14c, 108 }; 109 110 static const struct rcar_fuse_data rcar_fuse_v4h = { 111 .bank = 1, 112 .start = 0x100, 113 .end = 0x1a0, 114 }; 115 116 static const struct rcar_fuse_data rcar_fuse_v4m = { 117 .bank = 1, 118 .start = 0x100, 119 .end = 0x110, 120 }; 121 122 static const struct of_device_id rcar_fuse_match[] = { 123 { .compatible = "renesas,r8a779a0-efuse", .data = &rcar_fuse_v3u }, 124 { .compatible = "renesas,r8a779f0-efuse", .data = &rcar_fuse_s4 }, 125 { .compatible = "renesas,r8a779g0-otp", .data = &rcar_fuse_v4h }, 126 { .compatible = "renesas,r8a779h0-otp", .data = &rcar_fuse_v4m }, 127 { /* sentinel */ } 128 }; 129 MODULE_DEVICE_TABLE(of, rcar_fuse_match); 130 131 static struct platform_driver rcar_fuse_driver = { 132 .probe = rcar_fuse_probe, 133 .driver = { 134 .name = "rcar_fuse", 135 .of_match_table = rcar_fuse_match, 136 }, 137 }; 138 module_platform_driver(rcar_fuse_driver); 139 140 MODULE_DESCRIPTION("Renesas R-Car E-FUSE/OTP driver"); 141 MODULE_LICENSE("GPL"); 142 MODULE_AUTHOR("Geert Uytterhoeven"); 143