1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Amlogic Meson6, Meson8 and Meson8b eFuse Driver 4 * 5 * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 6 */ 7 8 #include <linux/bitfield.h> 9 #include <linux/bitops.h> 10 #include <linux/clk.h> 11 #include <linux/delay.h> 12 #include <linux/io.h> 13 #include <linux/iopoll.h> 14 #include <linux/module.h> 15 #include <linux/nvmem-provider.h> 16 #include <linux/of.h> 17 #include <linux/platform_device.h> 18 #include <linux/sizes.h> 19 #include <linux/slab.h> 20 21 #define MESON_MX_EFUSE_CNTL1 0x04 22 #define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27) 23 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26) 24 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25) 25 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24) 26 #define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16) 27 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14) 28 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13) 29 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12) 30 #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11) 31 #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0) 32 33 #define MESON_MX_EFUSE_CNTL2 0x08 34 35 #define MESON_MX_EFUSE_CNTL4 0x10 36 #define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10) 37 38 struct meson_mx_efuse_platform_data { 39 const char *name; 40 unsigned int word_size; 41 }; 42 43 struct meson_mx_efuse { 44 void __iomem *base; 45 struct clk *core_clk; 46 struct nvmem_device *nvmem; 47 struct nvmem_config config; 48 }; 49 50 static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg, 51 u32 mask, u32 set) 52 { 53 u32 data; 54 55 data = readl(efuse->base + reg); 56 data &= ~mask; 57 data |= (set & mask); 58 59 writel(data, efuse->base + reg); 60 } 61 62 static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse) 63 { 64 int err; 65 66 err = clk_prepare_enable(efuse->core_clk); 67 if (err) 68 return err; 69 70 /* power up the efuse */ 71 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 72 MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0); 73 74 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4, 75 MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0); 76 77 return 0; 78 } 79 80 static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse) 81 { 82 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 83 MESON_MX_EFUSE_CNTL1_PD_ENABLE, 84 MESON_MX_EFUSE_CNTL1_PD_ENABLE); 85 86 clk_disable_unprepare(efuse->core_clk); 87 } 88 89 static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse, 90 unsigned int addr, u32 *value) 91 { 92 int err; 93 u32 regval; 94 95 /* write the address to read */ 96 regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr); 97 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 98 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval); 99 100 /* inform the hardware that we changed the address */ 101 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 102 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 103 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET); 104 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 105 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0); 106 107 /* start the read process */ 108 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 109 MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 110 MESON_MX_EFUSE_CNTL1_AUTO_RD_START); 111 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 112 MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0); 113 114 /* 115 * perform a dummy read to ensure that the HW has the RD_BUSY bit set 116 * when polling for the status below. 117 */ 118 readl(efuse->base + MESON_MX_EFUSE_CNTL1); 119 120 err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1, 121 regval, 122 (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)), 123 1, 1000); 124 if (err) { 125 dev_err(efuse->config.dev, 126 "Timeout while reading efuse address %u\n", addr); 127 return err; 128 } 129 130 *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2); 131 132 return 0; 133 } 134 135 static int meson_mx_efuse_read(void *context, unsigned int offset, 136 void *buf, size_t bytes) 137 { 138 struct meson_mx_efuse *efuse = context; 139 u32 tmp; 140 int err, i, addr; 141 142 err = meson_mx_efuse_hw_enable(efuse); 143 if (err) 144 return err; 145 146 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 147 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 148 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE); 149 150 for (i = 0; i < bytes; i += efuse->config.word_size) { 151 addr = (offset + i) / efuse->config.word_size; 152 153 err = meson_mx_efuse_read_addr(efuse, addr, &tmp); 154 if (err) 155 break; 156 157 memcpy(buf + i, &tmp, 158 min_t(size_t, bytes - i, efuse->config.word_size)); 159 } 160 161 meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 162 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0); 163 164 meson_mx_efuse_hw_disable(efuse); 165 166 return err; 167 } 168 169 static const struct meson_mx_efuse_platform_data meson6_efuse_data = { 170 .name = "meson6-efuse", 171 .word_size = 1, 172 }; 173 174 static const struct meson_mx_efuse_platform_data meson8_efuse_data = { 175 .name = "meson8-efuse", 176 .word_size = 4, 177 }; 178 179 static const struct meson_mx_efuse_platform_data meson8b_efuse_data = { 180 .name = "meson8b-efuse", 181 .word_size = 4, 182 }; 183 184 static const struct of_device_id meson_mx_efuse_match[] = { 185 { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data }, 186 { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data }, 187 { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data }, 188 { /* sentinel */ }, 189 }; 190 MODULE_DEVICE_TABLE(of, meson_mx_efuse_match); 191 192 static int meson_mx_efuse_probe(struct platform_device *pdev) 193 { 194 const struct meson_mx_efuse_platform_data *drvdata; 195 struct meson_mx_efuse *efuse; 196 197 drvdata = of_device_get_match_data(&pdev->dev); 198 if (!drvdata) 199 return -EINVAL; 200 201 efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); 202 if (!efuse) 203 return -ENOMEM; 204 205 efuse->base = devm_platform_ioremap_resource(pdev, 0); 206 if (IS_ERR(efuse->base)) 207 return PTR_ERR(efuse->base); 208 209 efuse->config.name = drvdata->name; 210 efuse->config.owner = THIS_MODULE; 211 efuse->config.dev = &pdev->dev; 212 efuse->config.priv = efuse; 213 efuse->config.stride = drvdata->word_size; 214 efuse->config.word_size = drvdata->word_size; 215 efuse->config.size = SZ_512; 216 efuse->config.read_only = true; 217 efuse->config.reg_read = meson_mx_efuse_read; 218 219 efuse->core_clk = devm_clk_get(&pdev->dev, "core"); 220 if (IS_ERR(efuse->core_clk)) { 221 dev_err(&pdev->dev, "Failed to get core clock\n"); 222 return PTR_ERR(efuse->core_clk); 223 } 224 225 efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config); 226 227 return PTR_ERR_OR_ZERO(efuse->nvmem); 228 } 229 230 static struct platform_driver meson_mx_efuse_driver = { 231 .probe = meson_mx_efuse_probe, 232 .driver = { 233 .name = "meson-mx-efuse", 234 .of_match_table = meson_mx_efuse_match, 235 }, 236 }; 237 238 module_platform_driver(meson_mx_efuse_driver); 239 240 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); 241 MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); 242 MODULE_LICENSE("GPL v2"); 243