18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Amlogic Meson6, Meson8 and Meson8b eFuse Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/sizes.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1 0x04 238c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27) 248c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26) 258c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25) 268c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24) 278c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16) 288c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14) 298c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13) 308c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12) 318c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11) 328c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL2 0x08 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL4 0x10 378c2ecf20Sopenharmony_ci#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct meson_mx_efuse_platform_data { 408c2ecf20Sopenharmony_ci const char *name; 418c2ecf20Sopenharmony_ci unsigned int word_size; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct meson_mx_efuse { 458c2ecf20Sopenharmony_ci void __iomem *base; 468c2ecf20Sopenharmony_ci struct clk *core_clk; 478c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 488c2ecf20Sopenharmony_ci struct nvmem_config config; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg, 528c2ecf20Sopenharmony_ci u32 mask, u32 set) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 data; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci data = readl(efuse->base + reg); 578c2ecf20Sopenharmony_ci data &= ~mask; 588c2ecf20Sopenharmony_ci data |= (set & mask); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci writel(data, efuse->base + reg); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci err = clk_prepare_enable(efuse->core_clk); 688c2ecf20Sopenharmony_ci if (err) 698c2ecf20Sopenharmony_ci return err; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* power up the efuse */ 728c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 738c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4, 768c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 848c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_PD_ENABLE, 858c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_PD_ENABLE); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci clk_disable_unprepare(efuse->core_clk); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse, 918c2ecf20Sopenharmony_ci unsigned int addr, u32 *value) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int err; 948c2ecf20Sopenharmony_ci u32 regval; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* write the address to read */ 978c2ecf20Sopenharmony_ci regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr); 988c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 998c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* inform the hardware that we changed the address */ 1028c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1038c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 1048c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET); 1058c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1068c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* start the read process */ 1098c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1108c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 1118c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_START); 1128c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1138c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * perform a dummy read to ensure that the HW has the RD_BUSY bit set 1178c2ecf20Sopenharmony_ci * when polling for the status below. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci readl(efuse->base + MESON_MX_EFUSE_CNTL1); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1, 1228c2ecf20Sopenharmony_ci regval, 1238c2ecf20Sopenharmony_ci (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)), 1248c2ecf20Sopenharmony_ci 1, 1000); 1258c2ecf20Sopenharmony_ci if (err) { 1268c2ecf20Sopenharmony_ci dev_err(efuse->config.dev, 1278c2ecf20Sopenharmony_ci "Timeout while reading efuse address %u\n", addr); 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int meson_mx_efuse_read(void *context, unsigned int offset, 1378c2ecf20Sopenharmony_ci void *buf, size_t bytes) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct meson_mx_efuse *efuse = context; 1408c2ecf20Sopenharmony_ci u32 tmp; 1418c2ecf20Sopenharmony_ci int err, i, addr; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = meson_mx_efuse_hw_enable(efuse); 1448c2ecf20Sopenharmony_ci if (err) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1488c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 1498c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (i = 0; i < bytes; i += efuse->config.word_size) { 1528c2ecf20Sopenharmony_ci addr = (offset + i) / efuse->config.word_size; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci err = meson_mx_efuse_read_addr(efuse, addr, &tmp); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci memcpy(buf + i, &tmp, 1598c2ecf20Sopenharmony_ci min_t(size_t, bytes - i, efuse->config.word_size)); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, 1638c2ecf20Sopenharmony_ci MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci meson_mx_efuse_hw_disable(efuse); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return err; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct meson_mx_efuse_platform_data meson6_efuse_data = { 1718c2ecf20Sopenharmony_ci .name = "meson6-efuse", 1728c2ecf20Sopenharmony_ci .word_size = 1, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const struct meson_mx_efuse_platform_data meson8_efuse_data = { 1768c2ecf20Sopenharmony_ci .name = "meson8-efuse", 1778c2ecf20Sopenharmony_ci .word_size = 4, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic const struct meson_mx_efuse_platform_data meson8b_efuse_data = { 1818c2ecf20Sopenharmony_ci .name = "meson8b-efuse", 1828c2ecf20Sopenharmony_ci .word_size = 4, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct of_device_id meson_mx_efuse_match[] = { 1868c2ecf20Sopenharmony_ci { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data }, 1878c2ecf20Sopenharmony_ci { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data }, 1888c2ecf20Sopenharmony_ci { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data }, 1898c2ecf20Sopenharmony_ci { /* sentinel */ }, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_mx_efuse_match); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int meson_mx_efuse_probe(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci const struct meson_mx_efuse_platform_data *drvdata; 1968c2ecf20Sopenharmony_ci struct meson_mx_efuse *efuse; 1978c2ecf20Sopenharmony_ci struct resource *res; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci drvdata = of_device_get_match_data(&pdev->dev); 2008c2ecf20Sopenharmony_ci if (!drvdata) 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (!efuse) 2058c2ecf20Sopenharmony_ci return -ENOMEM; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2088c2ecf20Sopenharmony_ci efuse->base = devm_ioremap_resource(&pdev->dev, res); 2098c2ecf20Sopenharmony_ci if (IS_ERR(efuse->base)) 2108c2ecf20Sopenharmony_ci return PTR_ERR(efuse->base); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name, 2138c2ecf20Sopenharmony_ci GFP_KERNEL); 2148c2ecf20Sopenharmony_ci efuse->config.owner = THIS_MODULE; 2158c2ecf20Sopenharmony_ci efuse->config.dev = &pdev->dev; 2168c2ecf20Sopenharmony_ci efuse->config.priv = efuse; 2178c2ecf20Sopenharmony_ci efuse->config.stride = drvdata->word_size; 2188c2ecf20Sopenharmony_ci efuse->config.word_size = drvdata->word_size; 2198c2ecf20Sopenharmony_ci efuse->config.size = SZ_512; 2208c2ecf20Sopenharmony_ci efuse->config.read_only = true; 2218c2ecf20Sopenharmony_ci efuse->config.reg_read = meson_mx_efuse_read; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci efuse->core_clk = devm_clk_get(&pdev->dev, "core"); 2248c2ecf20Sopenharmony_ci if (IS_ERR(efuse->core_clk)) { 2258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get core clock\n"); 2268c2ecf20Sopenharmony_ci return PTR_ERR(efuse->core_clk); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(efuse->nvmem); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic struct platform_driver meson_mx_efuse_driver = { 2358c2ecf20Sopenharmony_ci .probe = meson_mx_efuse_probe, 2368c2ecf20Sopenharmony_ci .driver = { 2378c2ecf20Sopenharmony_ci .name = "meson-mx-efuse", 2388c2ecf20Sopenharmony_ci .of_match_table = meson_mx_efuse_match, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cimodule_platform_driver(meson_mx_efuse_driver); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); 2458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); 2468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 247