1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2988437aeSOleksij Rempel /* 3988437aeSOleksij Rempel * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> 4988437aeSOleksij Rempel * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 5988437aeSOleksij Rempel */ 6988437aeSOleksij Rempel 7988437aeSOleksij Rempel #include <linux/mfd/syscon.h> 8988437aeSOleksij Rempel #include <linux/module.h> 9988437aeSOleksij Rempel #include <linux/nvmem-provider.h> 10988437aeSOleksij Rempel #include <linux/of_device.h> 11988437aeSOleksij Rempel #include <linux/regmap.h> 12988437aeSOleksij Rempel 13988437aeSOleksij Rempel #define IMX6Q_SNVS_HPLR 0x00 14988437aeSOleksij Rempel #define IMX6Q_SNVS_LPLR 0x34 15988437aeSOleksij Rempel #define IMX6Q_SNVS_LPGPR 0x68 16988437aeSOleksij Rempel 1780b820caSAndrey Yurovsky #define IMX7D_SNVS_HPLR 0x00 1880b820caSAndrey Yurovsky #define IMX7D_SNVS_LPLR 0x34 1980b820caSAndrey Yurovsky #define IMX7D_SNVS_LPGPR 0x90 2080b820caSAndrey Yurovsky 2180b820caSAndrey Yurovsky #define IMX_GPR_SL BIT(5) 2280b820caSAndrey Yurovsky #define IMX_GPR_HL BIT(5) 2380b820caSAndrey Yurovsky 24988437aeSOleksij Rempel struct snvs_lpgpr_cfg { 25988437aeSOleksij Rempel int offset; 26988437aeSOleksij Rempel int offset_hplr; 27988437aeSOleksij Rempel int offset_lplr; 2880b820caSAndrey Yurovsky int size; 29988437aeSOleksij Rempel }; 30988437aeSOleksij Rempel 31988437aeSOleksij Rempel struct snvs_lpgpr_priv { 32988437aeSOleksij Rempel struct device_d *dev; 33988437aeSOleksij Rempel struct regmap *regmap; 34988437aeSOleksij Rempel struct nvmem_config cfg; 35988437aeSOleksij Rempel const struct snvs_lpgpr_cfg *dcfg; 36988437aeSOleksij Rempel }; 37988437aeSOleksij Rempel 38988437aeSOleksij Rempel static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { 39988437aeSOleksij Rempel .offset = IMX6Q_SNVS_LPGPR, 40988437aeSOleksij Rempel .offset_hplr = IMX6Q_SNVS_HPLR, 41988437aeSOleksij Rempel .offset_lplr = IMX6Q_SNVS_LPLR, 4280b820caSAndrey Yurovsky .size = 4, 4380b820caSAndrey Yurovsky }; 4480b820caSAndrey Yurovsky 4580b820caSAndrey Yurovsky static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = { 4680b820caSAndrey Yurovsky .offset = IMX7D_SNVS_LPGPR, 4780b820caSAndrey Yurovsky .offset_hplr = IMX7D_SNVS_HPLR, 4880b820caSAndrey Yurovsky .offset_lplr = IMX7D_SNVS_LPLR, 4980b820caSAndrey Yurovsky .size = 16, 50988437aeSOleksij Rempel }; 51988437aeSOleksij Rempel 52988437aeSOleksij Rempel static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, 53988437aeSOleksij Rempel size_t bytes) 54988437aeSOleksij Rempel { 55988437aeSOleksij Rempel struct snvs_lpgpr_priv *priv = context; 56988437aeSOleksij Rempel const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; 57988437aeSOleksij Rempel unsigned int lock_reg; 58988437aeSOleksij Rempel int ret; 59988437aeSOleksij Rempel 60988437aeSOleksij Rempel ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg); 61988437aeSOleksij Rempel if (ret < 0) 62988437aeSOleksij Rempel return ret; 63988437aeSOleksij Rempel 6480b820caSAndrey Yurovsky if (lock_reg & IMX_GPR_SL) 65988437aeSOleksij Rempel return -EPERM; 66988437aeSOleksij Rempel 67988437aeSOleksij Rempel ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); 68988437aeSOleksij Rempel if (ret < 0) 69988437aeSOleksij Rempel return ret; 70988437aeSOleksij Rempel 7180b820caSAndrey Yurovsky if (lock_reg & IMX_GPR_HL) 72988437aeSOleksij Rempel return -EPERM; 73988437aeSOleksij Rempel 74988437aeSOleksij Rempel return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, 75988437aeSOleksij Rempel bytes / 4); 76988437aeSOleksij Rempel } 77988437aeSOleksij Rempel 78988437aeSOleksij Rempel static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, 79988437aeSOleksij Rempel size_t bytes) 80988437aeSOleksij Rempel { 81988437aeSOleksij Rempel struct snvs_lpgpr_priv *priv = context; 82988437aeSOleksij Rempel const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; 83988437aeSOleksij Rempel 84988437aeSOleksij Rempel return regmap_bulk_read(priv->regmap, dcfg->offset + offset, 85988437aeSOleksij Rempel val, bytes / 4); 86988437aeSOleksij Rempel } 87988437aeSOleksij Rempel 88988437aeSOleksij Rempel static int snvs_lpgpr_probe(struct platform_device *pdev) 89988437aeSOleksij Rempel { 90988437aeSOleksij Rempel struct device *dev = &pdev->dev; 91988437aeSOleksij Rempel struct device_node *node = dev->of_node; 92988437aeSOleksij Rempel struct device_node *syscon_node; 93988437aeSOleksij Rempel struct snvs_lpgpr_priv *priv; 94988437aeSOleksij Rempel struct nvmem_config *cfg; 95988437aeSOleksij Rempel struct nvmem_device *nvmem; 96988437aeSOleksij Rempel const struct snvs_lpgpr_cfg *dcfg; 97988437aeSOleksij Rempel 98988437aeSOleksij Rempel if (!node) 99988437aeSOleksij Rempel return -ENOENT; 100988437aeSOleksij Rempel 101988437aeSOleksij Rempel priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 102988437aeSOleksij Rempel if (!priv) 103988437aeSOleksij Rempel return -ENOMEM; 104988437aeSOleksij Rempel 105988437aeSOleksij Rempel dcfg = of_device_get_match_data(dev); 106988437aeSOleksij Rempel if (!dcfg) 107988437aeSOleksij Rempel return -EINVAL; 108988437aeSOleksij Rempel 109988437aeSOleksij Rempel syscon_node = of_get_parent(node); 110988437aeSOleksij Rempel if (!syscon_node) 111988437aeSOleksij Rempel return -ENODEV; 112988437aeSOleksij Rempel 113988437aeSOleksij Rempel priv->regmap = syscon_node_to_regmap(syscon_node); 114988437aeSOleksij Rempel of_node_put(syscon_node); 115988437aeSOleksij Rempel if (IS_ERR(priv->regmap)) 116988437aeSOleksij Rempel return PTR_ERR(priv->regmap); 117988437aeSOleksij Rempel 118988437aeSOleksij Rempel priv->dcfg = dcfg; 119988437aeSOleksij Rempel 120988437aeSOleksij Rempel cfg = &priv->cfg; 121988437aeSOleksij Rempel cfg->priv = priv; 122988437aeSOleksij Rempel cfg->name = dev_name(dev); 123988437aeSOleksij Rempel cfg->dev = dev; 1240e189891SAndrey Smirnov cfg->stride = 4; 1250e189891SAndrey Smirnov cfg->word_size = 4; 126*e050f160SZheng Yongjun cfg->size = dcfg->size; 1270e189891SAndrey Smirnov cfg->owner = THIS_MODULE; 1280e189891SAndrey Smirnov cfg->reg_read = snvs_lpgpr_read; 1290e189891SAndrey Smirnov cfg->reg_write = snvs_lpgpr_write; 130988437aeSOleksij Rempel 1315557acf6SAndrey Smirnov nvmem = devm_nvmem_register(dev, cfg); 132988437aeSOleksij Rempel 1335557acf6SAndrey Smirnov return PTR_ERR_OR_ZERO(nvmem); 134988437aeSOleksij Rempel } 135988437aeSOleksij Rempel 136988437aeSOleksij Rempel static const struct of_device_id snvs_lpgpr_dt_ids[] = { 137988437aeSOleksij Rempel { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, 138988437aeSOleksij Rempel { .compatible = "fsl,imx6ul-snvs-lpgpr", 139988437aeSOleksij Rempel .data = &snvs_lpgpr_cfg_imx6q }, 14080b820caSAndrey Yurovsky { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d }, 141988437aeSOleksij Rempel { }, 142988437aeSOleksij Rempel }; 143988437aeSOleksij Rempel MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); 144988437aeSOleksij Rempel 145988437aeSOleksij Rempel static struct platform_driver snvs_lpgpr_driver = { 146988437aeSOleksij Rempel .probe = snvs_lpgpr_probe, 147988437aeSOleksij Rempel .driver = { 148988437aeSOleksij Rempel .name = "snvs_lpgpr", 149988437aeSOleksij Rempel .of_match_table = snvs_lpgpr_dt_ids, 150988437aeSOleksij Rempel }, 151988437aeSOleksij Rempel }; 152988437aeSOleksij Rempel module_platform_driver(snvs_lpgpr_driver); 153988437aeSOleksij Rempel 154988437aeSOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); 15580b820caSAndrey Yurovsky MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage"); 156988437aeSOleksij Rempel MODULE_LICENSE("GPL v2"); 157