244 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Amlogic Meson6, Meson8 and Meson8b eFuse Driver4 *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 0x0422#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 0x0834 35#define MESON_MX_EFUSE_CNTL4 0x1036#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 set115 * 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");244