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>
11 #include <linux/platform_device.h>
12 #include <linux/regmap.h>
14 #define IMX6Q_SNVS_HPLR 0x00
15 #define IMX6Q_SNVS_LPLR 0x34
16 #define IMX6Q_SNVS_LPGPR 0x68
18 #define IMX7D_SNVS_HPLR 0x00
19 #define IMX7D_SNVS_LPLR 0x34
20 #define IMX7D_SNVS_LPGPR 0x90
22 #define IMX_GPR_SL BIT(5)
23 #define IMX_GPR_HL BIT(5)
25 struct snvs_lpgpr_cfg
{
32 struct snvs_lpgpr_priv
{
34 struct regmap
*regmap
;
35 struct nvmem_config cfg
;
36 const struct snvs_lpgpr_cfg
*dcfg
;
39 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q
= {
40 .offset
= IMX6Q_SNVS_LPGPR
,
41 .offset_hplr
= IMX6Q_SNVS_HPLR
,
42 .offset_lplr
= IMX6Q_SNVS_LPLR
,
46 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d
= {
47 .offset
= IMX7D_SNVS_LPGPR
,
48 .offset_hplr
= IMX7D_SNVS_HPLR
,
49 .offset_lplr
= IMX7D_SNVS_LPLR
,
53 static int snvs_lpgpr_write(void *context
, unsigned int offset
, void *val
,
56 struct snvs_lpgpr_priv
*priv
= context
;
57 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
58 unsigned int lock_reg
;
61 ret
= regmap_read(priv
->regmap
, dcfg
->offset_hplr
, &lock_reg
);
65 if (lock_reg
& IMX_GPR_SL
)
68 ret
= regmap_read(priv
->regmap
, dcfg
->offset_lplr
, &lock_reg
);
72 if (lock_reg
& IMX_GPR_HL
)
75 return regmap_bulk_write(priv
->regmap
, dcfg
->offset
+ offset
, val
,
79 static int snvs_lpgpr_read(void *context
, unsigned int offset
, void *val
,
82 struct snvs_lpgpr_priv
*priv
= context
;
83 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
85 return regmap_bulk_read(priv
->regmap
, dcfg
->offset
+ offset
,
89 static int snvs_lpgpr_probe(struct platform_device
*pdev
)
91 struct device
*dev
= &pdev
->dev
;
92 struct device_node
*node
= dev
->of_node
;
93 struct device_node
*syscon_node
;
94 struct snvs_lpgpr_priv
*priv
;
95 struct nvmem_config
*cfg
;
96 struct nvmem_device
*nvmem
;
97 const struct snvs_lpgpr_cfg
*dcfg
;
102 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
106 dcfg
= of_device_get_match_data(dev
);
110 syscon_node
= of_get_parent(node
);
114 priv
->regmap
= syscon_node_to_regmap(syscon_node
);
115 of_node_put(syscon_node
);
116 if (IS_ERR(priv
->regmap
))
117 return PTR_ERR(priv
->regmap
);
123 cfg
->name
= dev_name(dev
);
127 cfg
->size
= dcfg
->size
;
128 cfg
->owner
= THIS_MODULE
;
129 cfg
->reg_read
= snvs_lpgpr_read
;
130 cfg
->reg_write
= snvs_lpgpr_write
;
132 nvmem
= devm_nvmem_register(dev
, cfg
);
134 return PTR_ERR_OR_ZERO(nvmem
);
137 static const struct of_device_id snvs_lpgpr_dt_ids
[] = {
138 { .compatible
= "fsl,imx6q-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx6q
},
139 { .compatible
= "fsl,imx6ul-snvs-lpgpr",
140 .data
= &snvs_lpgpr_cfg_imx6q
},
141 { .compatible
= "fsl,imx7d-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx7d
},
144 MODULE_DEVICE_TABLE(of
, snvs_lpgpr_dt_ids
);
146 static struct platform_driver snvs_lpgpr_driver
= {
147 .probe
= snvs_lpgpr_probe
,
149 .name
= "snvs_lpgpr",
150 .of_match_table
= snvs_lpgpr_dt_ids
,
153 module_platform_driver(snvs_lpgpr_driver
);
155 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
156 MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
157 MODULE_LICENSE("GPL v2");