2 * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
3 * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation.
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
12 #include <linux/nvmem-provider.h>
13 #include <linux/of_device.h>
14 #include <linux/regmap.h>
16 #define IMX6Q_SNVS_HPLR 0x00
17 #define IMX6Q_SNVS_LPLR 0x34
18 #define IMX6Q_SNVS_LPGPR 0x68
20 #define IMX7D_SNVS_HPLR 0x00
21 #define IMX7D_SNVS_LPLR 0x34
22 #define IMX7D_SNVS_LPGPR 0x90
24 #define IMX_GPR_SL BIT(5)
25 #define IMX_GPR_HL BIT(5)
27 struct snvs_lpgpr_cfg
{
34 struct snvs_lpgpr_priv
{
36 struct regmap
*regmap
;
37 struct nvmem_config cfg
;
38 const struct snvs_lpgpr_cfg
*dcfg
;
41 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q
= {
42 .offset
= IMX6Q_SNVS_LPGPR
,
43 .offset_hplr
= IMX6Q_SNVS_HPLR
,
44 .offset_lplr
= IMX6Q_SNVS_LPLR
,
48 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d
= {
49 .offset
= IMX7D_SNVS_LPGPR
,
50 .offset_hplr
= IMX7D_SNVS_HPLR
,
51 .offset_lplr
= IMX7D_SNVS_LPLR
,
55 static int snvs_lpgpr_write(void *context
, unsigned int offset
, void *val
,
58 struct snvs_lpgpr_priv
*priv
= context
;
59 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
60 unsigned int lock_reg
;
63 ret
= regmap_read(priv
->regmap
, dcfg
->offset_hplr
, &lock_reg
);
67 if (lock_reg
& IMX_GPR_SL
)
70 ret
= regmap_read(priv
->regmap
, dcfg
->offset_lplr
, &lock_reg
);
74 if (lock_reg
& IMX_GPR_HL
)
77 return regmap_bulk_write(priv
->regmap
, dcfg
->offset
+ offset
, val
,
81 static int snvs_lpgpr_read(void *context
, unsigned int offset
, void *val
,
84 struct snvs_lpgpr_priv
*priv
= context
;
85 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
87 return regmap_bulk_read(priv
->regmap
, dcfg
->offset
+ offset
,
91 static int snvs_lpgpr_probe(struct platform_device
*pdev
)
93 struct device
*dev
= &pdev
->dev
;
94 struct device_node
*node
= dev
->of_node
;
95 struct device_node
*syscon_node
;
96 struct snvs_lpgpr_priv
*priv
;
97 struct nvmem_config
*cfg
;
98 struct nvmem_device
*nvmem
;
99 const struct snvs_lpgpr_cfg
*dcfg
;
104 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
108 dcfg
= of_device_get_match_data(dev
);
112 syscon_node
= of_get_parent(node
);
116 priv
->regmap
= syscon_node_to_regmap(syscon_node
);
117 of_node_put(syscon_node
);
118 if (IS_ERR(priv
->regmap
))
119 return PTR_ERR(priv
->regmap
);
125 cfg
->name
= dev_name(dev
);
129 cfg
->size
= dcfg
->size
,
130 cfg
->owner
= THIS_MODULE
;
131 cfg
->reg_read
= snvs_lpgpr_read
;
132 cfg
->reg_write
= snvs_lpgpr_write
;
134 nvmem
= devm_nvmem_register(dev
, cfg
);
136 return PTR_ERR_OR_ZERO(nvmem
);
139 static const struct of_device_id snvs_lpgpr_dt_ids
[] = {
140 { .compatible
= "fsl,imx6q-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx6q
},
141 { .compatible
= "fsl,imx6ul-snvs-lpgpr",
142 .data
= &snvs_lpgpr_cfg_imx6q
},
143 { .compatible
= "fsl,imx7d-snvs-lpgpr", .data
= &snvs_lpgpr_cfg_imx7d
},
146 MODULE_DEVICE_TABLE(of
, snvs_lpgpr_dt_ids
);
148 static struct platform_driver snvs_lpgpr_driver
= {
149 .probe
= snvs_lpgpr_probe
,
151 .name
= "snvs_lpgpr",
152 .of_match_table
= snvs_lpgpr_dt_ids
,
155 module_platform_driver(snvs_lpgpr_driver
);
157 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
158 MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
159 MODULE_LICENSE("GPL v2");