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