Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Amlogic Meson6, Meson8 and Meson8b eFuse Driver
0004  *
0005  * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
0006  */
0007 
0008 #include <linux/bitfield.h>
0009 #include <linux/bitops.h>
0010 #include <linux/clk.h>
0011 #include <linux/delay.h>
0012 #include <linux/io.h>
0013 #include <linux/iopoll.h>
0014 #include <linux/module.h>
0015 #include <linux/nvmem-provider.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/sizes.h>
0020 #include <linux/slab.h>
0021 
0022 #define MESON_MX_EFUSE_CNTL1                    0x04
0023 #define MESON_MX_EFUSE_CNTL1_PD_ENABLE              BIT(27)
0024 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY           BIT(26)
0025 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_START          BIT(25)
0026 #define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE         BIT(24)
0027 #define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA           GENMASK(23, 16)
0028 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY           BIT(14)
0029 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_START          BIT(13)
0030 #define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE         BIT(12)
0031 #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET          BIT(11)
0032 #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK         GENMASK(10, 0)
0033 
0034 #define MESON_MX_EFUSE_CNTL2                    0x08
0035 
0036 #define MESON_MX_EFUSE_CNTL4                    0x10
0037 #define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE         BIT(10)
0038 
0039 struct meson_mx_efuse_platform_data {
0040     const char *name;
0041     unsigned int word_size;
0042 };
0043 
0044 struct meson_mx_efuse {
0045     void __iomem *base;
0046     struct clk *core_clk;
0047     struct nvmem_device *nvmem;
0048     struct nvmem_config config;
0049 };
0050 
0051 static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg,
0052                      u32 mask, u32 set)
0053 {
0054     u32 data;
0055 
0056     data = readl(efuse->base + reg);
0057     data &= ~mask;
0058     data |= (set & mask);
0059 
0060     writel(data, efuse->base + reg);
0061 }
0062 
0063 static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse)
0064 {
0065     int err;
0066 
0067     err = clk_prepare_enable(efuse->core_clk);
0068     if (err)
0069         return err;
0070 
0071     /* power up the efuse */
0072     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0073                  MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0);
0074 
0075     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4,
0076                  MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0);
0077 
0078     return 0;
0079 }
0080 
0081 static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse)
0082 {
0083     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0084                  MESON_MX_EFUSE_CNTL1_PD_ENABLE,
0085                  MESON_MX_EFUSE_CNTL1_PD_ENABLE);
0086 
0087     clk_disable_unprepare(efuse->core_clk);
0088 }
0089 
0090 static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse,
0091                     unsigned int addr, u32 *value)
0092 {
0093     int err;
0094     u32 regval;
0095 
0096     /* write the address to read */
0097     regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr);
0098     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0099                  MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval);
0100 
0101     /* inform the hardware that we changed the address */
0102     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0103                  MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET,
0104                  MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET);
0105     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0106                  MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0);
0107 
0108     /* start the read process */
0109     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0110                  MESON_MX_EFUSE_CNTL1_AUTO_RD_START,
0111                  MESON_MX_EFUSE_CNTL1_AUTO_RD_START);
0112     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0113                  MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0);
0114 
0115     /*
0116      * perform a dummy read to ensure that the HW has the RD_BUSY bit set
0117      * when polling for the status below.
0118      */
0119     readl(efuse->base + MESON_MX_EFUSE_CNTL1);
0120 
0121     err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1,
0122             regval,
0123             (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)),
0124             1, 1000);
0125     if (err) {
0126         dev_err(efuse->config.dev,
0127             "Timeout while reading efuse address %u\n", addr);
0128         return err;
0129     }
0130 
0131     *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2);
0132 
0133     return 0;
0134 }
0135 
0136 static int meson_mx_efuse_read(void *context, unsigned int offset,
0137                    void *buf, size_t bytes)
0138 {
0139     struct meson_mx_efuse *efuse = context;
0140     u32 tmp;
0141     int err, i, addr;
0142 
0143     err = meson_mx_efuse_hw_enable(efuse);
0144     if (err)
0145         return err;
0146 
0147     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0148                  MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE,
0149                  MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE);
0150 
0151     for (i = 0; i < bytes; i += efuse->config.word_size) {
0152         addr = (offset + i) / efuse->config.word_size;
0153 
0154         err = meson_mx_efuse_read_addr(efuse, addr, &tmp);
0155         if (err)
0156             break;
0157 
0158         memcpy(buf + i, &tmp,
0159                min_t(size_t, bytes - i, efuse->config.word_size));
0160     }
0161 
0162     meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
0163                  MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0);
0164 
0165     meson_mx_efuse_hw_disable(efuse);
0166 
0167     return err;
0168 }
0169 
0170 static const struct meson_mx_efuse_platform_data meson6_efuse_data = {
0171     .name = "meson6-efuse",
0172     .word_size = 1,
0173 };
0174 
0175 static const struct meson_mx_efuse_platform_data meson8_efuse_data = {
0176     .name = "meson8-efuse",
0177     .word_size = 4,
0178 };
0179 
0180 static const struct meson_mx_efuse_platform_data meson8b_efuse_data = {
0181     .name = "meson8b-efuse",
0182     .word_size = 4,
0183 };
0184 
0185 static const struct of_device_id meson_mx_efuse_match[] = {
0186     { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data },
0187     { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data },
0188     { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data },
0189     { /* sentinel */ },
0190 };
0191 MODULE_DEVICE_TABLE(of, meson_mx_efuse_match);
0192 
0193 static int meson_mx_efuse_probe(struct platform_device *pdev)
0194 {
0195     const struct meson_mx_efuse_platform_data *drvdata;
0196     struct meson_mx_efuse *efuse;
0197     struct resource *res;
0198 
0199     drvdata = of_device_get_match_data(&pdev->dev);
0200     if (!drvdata)
0201         return -EINVAL;
0202 
0203     efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
0204     if (!efuse)
0205         return -ENOMEM;
0206 
0207     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0208     efuse->base = devm_ioremap_resource(&pdev->dev, res);
0209     if (IS_ERR(efuse->base))
0210         return PTR_ERR(efuse->base);
0211 
0212     efuse->config.name = drvdata->name;
0213     efuse->config.owner = THIS_MODULE;
0214     efuse->config.dev = &pdev->dev;
0215     efuse->config.priv = efuse;
0216     efuse->config.stride = drvdata->word_size;
0217     efuse->config.word_size = drvdata->word_size;
0218     efuse->config.size = SZ_512;
0219     efuse->config.read_only = true;
0220     efuse->config.reg_read = meson_mx_efuse_read;
0221 
0222     efuse->core_clk = devm_clk_get(&pdev->dev, "core");
0223     if (IS_ERR(efuse->core_clk)) {
0224         dev_err(&pdev->dev, "Failed to get core clock\n");
0225         return PTR_ERR(efuse->core_clk);
0226     }
0227 
0228     efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
0229 
0230     return PTR_ERR_OR_ZERO(efuse->nvmem);
0231 }
0232 
0233 static struct platform_driver meson_mx_efuse_driver = {
0234     .probe = meson_mx_efuse_probe,
0235     .driver = {
0236         .name = "meson-mx-efuse",
0237         .of_match_table = meson_mx_efuse_match,
0238     },
0239 };
0240 
0241 module_platform_driver(meson_mx_efuse_driver);
0242 
0243 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
0244 MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver");
0245 MODULE_LICENSE("GPL v2");