1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
4 * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
7 #include <linux/mfd/syscon.h>
8 #include <linux/module.h>
9 #include <linux/nvmem-provider.h>
10 #include <linux/of_device.h>
11 #include <linux/regmap.h>
13 #define IMX6Q_SNVS_HPLR 0x00
14 #define IMX6Q_SNVS_LPLR 0x34
15 #define IMX6Q_SNVS_LPGPR 0x68
17 #define IMX7D_SNVS_HPLR 0x00
18 #define IMX7D_SNVS_LPLR 0x34
19 #define IMX7D_SNVS_LPGPR 0x90
21 #define IMX_GPR_SL BIT(5)
22 #define IMX_GPR_HL BIT(5)
24 struct snvs_lpgpr_cfg
{
31 struct snvs_lpgpr_priv
{
33 struct regmap
*regmap
;
34 struct nvmem_config cfg
;
35 const struct snvs_lpgpr_cfg
*dcfg
;
38 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q
= {
39 .offset
= IMX6Q_SNVS_LPGPR
,
40 .offset_hplr
= IMX6Q_SNVS_HPLR
,
41 .offset_lplr
= IMX6Q_SNVS_LPLR
,
45 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d
= {
46 .offset
= IMX7D_SNVS_LPGPR
,
47 .offset_hplr
= IMX7D_SNVS_HPLR
,
48 .offset_lplr
= IMX7D_SNVS_LPLR
,
52 static int snvs_lpgpr_write(void *context
, unsigned int offset
, void *val
,
55 struct snvs_lpgpr_priv
*priv
= context
;
56 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
57 unsigned int lock_reg
;
60 ret
= regmap_read(priv
->regmap
, dcfg
->offset_hplr
, &lock_reg
);
64 if (lock_reg
& IMX_GPR_SL
)
67 ret
= regmap_read(priv
->regmap
, dcfg
->offset_lplr
, &lock_reg
);
71 if (lock_reg
& IMX_GPR_HL
)
74 return regmap_bulk_write(priv
->regmap
, dcfg
->offset
+ offset
, val
,
78 static int snvs_lpgpr_read(void *context
, unsigned int offset
, void *val
,
81 struct snvs_lpgpr_priv
*priv
= context
;
82 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
84 return regmap_bulk_read(priv
->regmap
, dcfg
->offset
+ offset
,
88 static int snvs_lpgpr_probe(struct platform_device
*pdev
)
90 struct device
*dev
= &pdev
->dev
;
91 struct device_node
*node
= dev
->of_node
;
92 struct device_node
*syscon_node
;
93 struct snvs_lpgpr_priv
*priv
;
94 struct nvmem_config
*cfg
;
95 struct nvmem_device
*nvmem
;
96 const struct snvs_lpgpr_cfg
*dcfg
;
101 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
105 dcfg
= of_device_get_match_data(dev
);
109 syscon_node
= of_get_parent(node
);
113 priv
->regmap
= syscon_node_to_regmap(syscon_node
);
114 of_node_put(syscon_node
);
115 if (IS_ERR(priv
->regmap
))
116 return PTR_ERR(priv
->regmap
);
122 cfg
->name
= dev_name(dev
);
126 cfg
->size
= dcfg
->size
,
127 cfg
->owner
= THIS_MODULE
;
128 cfg
->reg_read
= snvs_lpgpr_read
;
129 cfg
->reg_write
= snvs_lpgpr_write
;
131 nvmem
= devm_nvmem_register(dev
, cfg
);
133 return PTR_ERR_OR_ZERO(nvmem
);
136 static const struct of_device_id snvs_lpgpr_dt_ids
[] = {
137 { .compatible
= "fsl,imx6q-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx6q
},
138 { .compatible
= "fsl,imx6ul-snvs-lpgpr",
139 .data
= &snvs_lpgpr_cfg_imx6q
},
140 { .compatible
= "fsl,imx7d-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx7d
},
143 MODULE_DEVICE_TABLE(of
, snvs_lpgpr_dt_ids
);
145 static struct platform_driver snvs_lpgpr_driver
= {
146 .probe
= snvs_lpgpr_probe
,
148 .name
= "snvs_lpgpr",
149 .of_match_table
= snvs_lpgpr_dt_ids
,
152 module_platform_driver(snvs_lpgpr_driver
);
154 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
155 MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
156 MODULE_LICENSE("GPL v2");