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