^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/mfd/syscon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/nvmem-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #define IMX6Q_SNVS_HPLR 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #define IMX6Q_SNVS_LPLR 0x34
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #define IMX6Q_SNVS_LPGPR 0x68
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #define IMX7D_SNVS_HPLR 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define IMX7D_SNVS_LPLR 0x34
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define IMX7D_SNVS_LPGPR 0x90
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define IMX_GPR_SL BIT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define IMX_GPR_HL BIT(5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct snvs_lpgpr_cfg {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) int offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) int offset_hplr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) int offset_lplr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) int size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct snvs_lpgpr_priv {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct device_d *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) struct regmap *regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct nvmem_config cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) const struct snvs_lpgpr_cfg *dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) .offset = IMX6Q_SNVS_LPGPR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) .offset_hplr = IMX6Q_SNVS_HPLR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) .offset_lplr = IMX6Q_SNVS_LPLR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) .size = 4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) .offset = IMX7D_SNVS_LPGPR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) .offset_hplr = IMX7D_SNVS_HPLR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) .offset_lplr = IMX7D_SNVS_LPLR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .size = 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) size_t bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) struct snvs_lpgpr_priv *priv = context;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) unsigned int lock_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (lock_reg & IMX_GPR_SL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (lock_reg & IMX_GPR_HL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) bytes / 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) size_t bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct snvs_lpgpr_priv *priv = context;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return regmap_bulk_read(priv->regmap, dcfg->offset + offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) val, bytes / 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) static int snvs_lpgpr_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct device_node *node = dev->of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct device_node *syscon_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) struct snvs_lpgpr_priv *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) struct nvmem_config *cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct nvmem_device *nvmem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) const struct snvs_lpgpr_cfg *dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (!node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) dcfg = of_device_get_match_data(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) if (!dcfg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) syscon_node = of_get_parent(node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (!syscon_node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) priv->regmap = syscon_node_to_regmap(syscon_node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) of_node_put(syscon_node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) if (IS_ERR(priv->regmap))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) return PTR_ERR(priv->regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) priv->dcfg = dcfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) cfg = &priv->cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) cfg->priv = priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) cfg->name = dev_name(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) cfg->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) cfg->stride = 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) cfg->word_size = 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) cfg->size = dcfg->size,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) cfg->owner = THIS_MODULE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) cfg->reg_read = snvs_lpgpr_read;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) cfg->reg_write = snvs_lpgpr_write;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) nvmem = devm_nvmem_register(dev, cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) return PTR_ERR_OR_ZERO(nvmem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) static const struct of_device_id snvs_lpgpr_dt_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) { .compatible = "fsl,imx6ul-snvs-lpgpr",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) .data = &snvs_lpgpr_cfg_imx6q },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) { },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static struct platform_driver snvs_lpgpr_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .probe = snvs_lpgpr_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .name = "snvs_lpgpr",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .of_match_table = snvs_lpgpr_dt_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) module_platform_driver(snvs_lpgpr_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) MODULE_LICENSE("GPL v2");