Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
0004  * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
0005  */
0006 
0007 #include <linux/mfd/syscon.h>
0008 #include <linux/module.h>
0009 #include <linux/nvmem-provider.h>
0010 #include <linux/of_device.h>
0011 #include <linux/regmap.h>
0012 
0013 #define IMX6Q_SNVS_HPLR     0x00
0014 #define IMX6Q_SNVS_LPLR     0x34
0015 #define IMX6Q_SNVS_LPGPR    0x68
0016 
0017 #define IMX7D_SNVS_HPLR     0x00
0018 #define IMX7D_SNVS_LPLR     0x34
0019 #define IMX7D_SNVS_LPGPR    0x90
0020 
0021 #define IMX_GPR_SL      BIT(5)
0022 #define IMX_GPR_HL      BIT(5)
0023 
0024 struct snvs_lpgpr_cfg {
0025     int offset;
0026     int offset_hplr;
0027     int offset_lplr;
0028     int size;
0029 };
0030 
0031 struct snvs_lpgpr_priv {
0032     struct device_d         *dev;
0033     struct regmap           *regmap;
0034     struct nvmem_config     cfg;
0035     const struct snvs_lpgpr_cfg *dcfg;
0036 };
0037 
0038 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
0039     .offset     = IMX6Q_SNVS_LPGPR,
0040     .offset_hplr    = IMX6Q_SNVS_HPLR,
0041     .offset_lplr    = IMX6Q_SNVS_LPLR,
0042     .size       = 4,
0043 };
0044 
0045 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = {
0046     .offset     = IMX7D_SNVS_LPGPR,
0047     .offset_hplr    = IMX7D_SNVS_HPLR,
0048     .offset_lplr    = IMX7D_SNVS_LPLR,
0049     .size       = 16,
0050 };
0051 
0052 static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
0053                 size_t bytes)
0054 {
0055     struct snvs_lpgpr_priv *priv = context;
0056     const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
0057     unsigned int lock_reg;
0058     int ret;
0059 
0060     ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg);
0061     if (ret < 0)
0062         return ret;
0063 
0064     if (lock_reg & IMX_GPR_SL)
0065         return -EPERM;
0066 
0067     ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
0068     if (ret < 0)
0069         return ret;
0070 
0071     if (lock_reg & IMX_GPR_HL)
0072         return -EPERM;
0073 
0074     return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
0075                 bytes / 4);
0076 }
0077 
0078 static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,
0079                size_t bytes)
0080 {
0081     struct snvs_lpgpr_priv *priv = context;
0082     const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
0083 
0084     return regmap_bulk_read(priv->regmap, dcfg->offset + offset,
0085                    val, bytes / 4);
0086 }
0087 
0088 static int snvs_lpgpr_probe(struct platform_device *pdev)
0089 {
0090     struct device *dev = &pdev->dev;
0091     struct device_node *node = dev->of_node;
0092     struct device_node *syscon_node;
0093     struct snvs_lpgpr_priv *priv;
0094     struct nvmem_config *cfg;
0095     struct nvmem_device *nvmem;
0096     const struct snvs_lpgpr_cfg *dcfg;
0097 
0098     if (!node)
0099         return -ENOENT;
0100 
0101     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0102     if (!priv)
0103         return -ENOMEM;
0104 
0105     dcfg = of_device_get_match_data(dev);
0106     if (!dcfg)
0107         return -EINVAL;
0108 
0109     syscon_node = of_get_parent(node);
0110     if (!syscon_node)
0111         return -ENODEV;
0112 
0113     priv->regmap = syscon_node_to_regmap(syscon_node);
0114     of_node_put(syscon_node);
0115     if (IS_ERR(priv->regmap))
0116         return PTR_ERR(priv->regmap);
0117 
0118     priv->dcfg = dcfg;
0119 
0120     cfg = &priv->cfg;
0121     cfg->priv = priv;
0122     cfg->name = dev_name(dev);
0123     cfg->dev = dev;
0124     cfg->stride = 4;
0125     cfg->word_size = 4;
0126     cfg->size = dcfg->size;
0127     cfg->owner = THIS_MODULE;
0128     cfg->reg_read  = snvs_lpgpr_read;
0129     cfg->reg_write = snvs_lpgpr_write;
0130 
0131     nvmem = devm_nvmem_register(dev, cfg);
0132 
0133     return PTR_ERR_OR_ZERO(nvmem);
0134 }
0135 
0136 static const struct of_device_id snvs_lpgpr_dt_ids[] = {
0137     { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
0138     { .compatible = "fsl,imx6ul-snvs-lpgpr",
0139       .data = &snvs_lpgpr_cfg_imx6q },
0140     { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d },
0141     { },
0142 };
0143 MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
0144 
0145 static struct platform_driver snvs_lpgpr_driver = {
0146     .probe  = snvs_lpgpr_probe,
0147     .driver = {
0148         .name   = "snvs_lpgpr",
0149         .of_match_table = snvs_lpgpr_dt_ids,
0150     },
0151 };
0152 module_platform_driver(snvs_lpgpr_driver);
0153 
0154 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
0155 MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
0156 MODULE_LICENSE("GPL v2");