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_GPR_SL BIT(5)
18 #define IMX6Q_SNVS_LPLR 0x34
19 #define IMX6Q_GPR_HL BIT(5)
20 #define IMX6Q_SNVS_LPGPR 0x68
22 struct snvs_lpgpr_cfg
{
28 struct snvs_lpgpr_priv
{
30 struct regmap
*regmap
;
31 struct nvmem_config cfg
;
32 const struct snvs_lpgpr_cfg
*dcfg
;
35 static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q
= {
36 .offset
= IMX6Q_SNVS_LPGPR
,
37 .offset_hplr
= IMX6Q_SNVS_HPLR
,
38 .offset_lplr
= IMX6Q_SNVS_LPLR
,
41 static int snvs_lpgpr_write(void *context
, unsigned int offset
, void *val
,
44 struct snvs_lpgpr_priv
*priv
= context
;
45 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
46 unsigned int lock_reg
;
49 ret
= regmap_read(priv
->regmap
, dcfg
->offset_hplr
, &lock_reg
);
53 if (lock_reg
& IMX6Q_GPR_SL
)
56 ret
= regmap_read(priv
->regmap
, dcfg
->offset_lplr
, &lock_reg
);
60 if (lock_reg
& IMX6Q_GPR_HL
)
63 return regmap_bulk_write(priv
->regmap
, dcfg
->offset
+ offset
, val
,
67 static int snvs_lpgpr_read(void *context
, unsigned int offset
, void *val
,
70 struct snvs_lpgpr_priv
*priv
= context
;
71 const struct snvs_lpgpr_cfg
*dcfg
= priv
->dcfg
;
73 return regmap_bulk_read(priv
->regmap
, dcfg
->offset
+ offset
,
77 static int snvs_lpgpr_probe(struct platform_device
*pdev
)
79 struct device
*dev
= &pdev
->dev
;
80 struct device_node
*node
= dev
->of_node
;
81 struct device_node
*syscon_node
;
82 struct snvs_lpgpr_priv
*priv
;
83 struct nvmem_config
*cfg
;
84 struct nvmem_device
*nvmem
;
85 const struct snvs_lpgpr_cfg
*dcfg
;
90 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
94 dcfg
= of_device_get_match_data(dev
);
98 syscon_node
= of_get_parent(node
);
102 priv
->regmap
= syscon_node_to_regmap(syscon_node
);
103 of_node_put(syscon_node
);
104 if (IS_ERR(priv
->regmap
))
105 return PTR_ERR(priv
->regmap
);
111 cfg
->name
= dev_name(dev
);
116 cfg
->owner
= THIS_MODULE
,
117 cfg
->reg_read
= snvs_lpgpr_read
,
118 cfg
->reg_write
= snvs_lpgpr_write
,
120 nvmem
= nvmem_register(cfg
);
122 return PTR_ERR(nvmem
);
124 platform_set_drvdata(pdev
, nvmem
);
129 static int snvs_lpgpr_remove(struct platform_device
*pdev
)
131 struct nvmem_device
*nvmem
= platform_get_drvdata(pdev
);
133 return nvmem_unregister(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
},
142 MODULE_DEVICE_TABLE(of
, snvs_lpgpr_dt_ids
);
144 static struct platform_driver snvs_lpgpr_driver
= {
145 .probe
= snvs_lpgpr_probe
,
146 .remove
= snvs_lpgpr_remove
,
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 Secure Non-Volatile Storage");
156 MODULE_LICENSE("GPL v2");