Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.1-rc3 253 lines 7.1 kB view raw
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 47struct meson_mx_efuse_platform_data { 48 const char *name; 49 unsigned int word_size; 50}; 51 52struct meson_mx_efuse { 53 void __iomem *base; 54 struct clk *core_clk; 55 struct nvmem_device *nvmem; 56 struct nvmem_config config; 57}; 58 59static 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 71static 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 89static 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 98static 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 144static 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 177static const struct meson_mx_efuse_platform_data meson6_efuse_data = { 178 .name = "meson6-efuse", 179 .word_size = 1, 180}; 181 182static const struct meson_mx_efuse_platform_data meson8_efuse_data = { 183 .name = "meson8-efuse", 184 .word_size = 4, 185}; 186 187static const struct meson_mx_efuse_platform_data meson8b_efuse_data = { 188 .name = "meson8b-efuse", 189 .word_size = 4, 190}; 191 192static 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}; 198MODULE_DEVICE_TABLE(of, meson_mx_efuse_match); 199 200static 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 241static 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 249module_platform_driver(meson_mx_efuse_driver); 250 251MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); 252MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); 253MODULE_LICENSE("GPL v2");