0001
0002
0003
0004
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");