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