Blame view

drivers/nvmem/stm32-romem.c 4.81 KB
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
1
2
3
4
5
6
7
  // SPDX-License-Identifier: GPL-2.0
  /*
   * STM32 Factory-programmed memory read access driver
   *
   * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
   * Author: Fabrice Gasnier <fabrice.gasnier@st.com> for STMicroelectronics.
   */
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
8
  #include <linux/arm-smccc.h>
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
9
10
11
12
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/nvmem-provider.h>
  #include <linux/of_device.h>
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /* BSEC secure service access from non-secure */
  #define STM32_SMC_BSEC			0x82001003
  #define STM32_SMC_READ_SHADOW		0x01
  #define STM32_SMC_PROG_OTP		0x02
  #define STM32_SMC_WRITE_SHADOW		0x03
  #define STM32_SMC_READ_OTP		0x04
  
  /* shadow registers offest */
  #define STM32MP15_BSEC_DATA0		0x200
  
  /* 32 (x 32-bits) lower shadow registers */
  #define STM32MP15_BSEC_NUM_LOWER	32
  
  struct stm32_romem_cfg {
  	int size;
  };
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  struct stm32_romem_priv {
  	void __iomem *base;
  	struct nvmem_config cfg;
  };
  
  static int stm32_romem_read(void *context, unsigned int offset, void *buf,
  			    size_t bytes)
  {
  	struct stm32_romem_priv *priv = context;
  	u8 *buf8 = buf;
  	int i;
  
  	for (i = offset; i < offset + bytes; i++)
  		*buf8++ = readb_relaxed(priv->base + i);
  
  	return 0;
  }
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  static int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result)
  {
  #if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)
  	struct arm_smccc_res res;
  
  	arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res);
  	if (res.a0)
  		return -EIO;
  
  	if (result)
  		*result = (u32)res.a1;
  
  	return 0;
  #else
  	return -ENXIO;
  #endif
  }
  
  static int stm32_bsec_read(void *context, unsigned int offset, void *buf,
  			   size_t bytes)
  {
  	struct stm32_romem_priv *priv = context;
  	struct device *dev = priv->cfg.dev;
  	u32 roffset, rbytes, val;
  	u8 *buf8 = buf, *val8 = (u8 *)&val;
  	int i, j = 0, ret, skip_bytes, size;
  
  	/* Round unaligned access to 32-bits */
  	roffset = rounddown(offset, 4);
  	skip_bytes = offset & 0x3;
  	rbytes = roundup(bytes + skip_bytes, 4);
  
  	if (roffset + rbytes > priv->cfg.size)
  		return -EINVAL;
  
  	for (i = roffset; (i < roffset + rbytes); i += 4) {
  		u32 otp = i >> 2;
  
  		if (otp < STM32MP15_BSEC_NUM_LOWER) {
  			/* read lower data from shadow registers */
  			val = readl_relaxed(
  				priv->base + STM32MP15_BSEC_DATA0 + i);
  		} else {
  			ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0,
  					     &val);
  			if (ret) {
  				dev_err(dev, "Can't read data%d (%d)
  ", otp,
  					ret);
  				return ret;
  			}
  		}
  		/* skip first bytes in case of unaligned read */
  		if (skip_bytes)
  			size = min(bytes, (size_t)(4 - skip_bytes));
  		else
  			size = min(bytes, (size_t)4);
  		memcpy(&buf8[j], &val8[skip_bytes], size);
  		bytes -= size;
  		j += size;
  		skip_bytes = 0;
  	}
  
  	return 0;
  }
  
  static int stm32_bsec_write(void *context, unsigned int offset, void *buf,
  			    size_t bytes)
  {
  	struct stm32_romem_priv *priv = context;
  	struct device *dev = priv->cfg.dev;
  	u32 *buf32 = buf;
  	int ret, i;
  
  	/* Allow only writing complete 32-bits aligned words */
  	if ((bytes % 4) || (offset % 4))
  		return -EINVAL;
  
  	for (i = offset; i < offset + bytes; i += 4) {
  		ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++,
  				     NULL);
  		if (ret) {
  			dev_err(dev, "Can't write data%d (%d)
  ", i >> 2, ret);
  			return ret;
  		}
  	}
  
  	return 0;
  }
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
136
137
  static int stm32_romem_probe(struct platform_device *pdev)
  {
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
138
  	const struct stm32_romem_cfg *cfg;
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  	struct device *dev = &pdev->dev;
  	struct stm32_romem_priv *priv;
  	struct resource *res;
  
  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  	if (!priv)
  		return -ENOMEM;
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	priv->base = devm_ioremap_resource(dev, res);
  	if (IS_ERR(priv->base))
  		return PTR_ERR(priv->base);
  
  	priv->cfg.name = "stm32-romem";
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
153
154
  	priv->cfg.word_size = 1;
  	priv->cfg.stride = 1;
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
155
156
157
  	priv->cfg.dev = dev;
  	priv->cfg.priv = priv;
  	priv->cfg.owner = THIS_MODULE;
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
158
159
160
161
162
163
164
165
166
167
168
  	cfg = (const struct stm32_romem_cfg *)
  		of_match_device(dev->driver->of_match_table, dev)->data;
  	if (!cfg) {
  		priv->cfg.read_only = true;
  		priv->cfg.size = resource_size(res);
  		priv->cfg.reg_read = stm32_romem_read;
  	} else {
  		priv->cfg.size = cfg->size;
  		priv->cfg.reg_read = stm32_bsec_read;
  		priv->cfg.reg_write = stm32_bsec_write;
  	}
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
169
170
  	return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg));
  }
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
171
172
173
  static const struct stm32_romem_cfg stm32mp15_bsec_cfg = {
  	.size = 384, /* 96 x 32-bits data words */
  };
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
174
  static const struct of_device_id stm32_romem_of_match[] = {
7c1cd8fda   Fabrice Gasnier   nvmem: stm32: add...
175
176
177
178
179
  	{ .compatible = "st,stm32f4-otp", }, {
  		.compatible = "st,stm32mp15-bsec",
  		.data = (void *)&stm32mp15_bsec_cfg,
  	}, {
  	},
ded1b7fc2   Fabrice Gasnier   nvmem: Add driver...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  };
  MODULE_DEVICE_TABLE(of, stm32_romem_of_match);
  
  static struct platform_driver stm32_romem_driver = {
  	.probe = stm32_romem_probe,
  	.driver = {
  		.name = "stm32-romem",
  		.of_match_table = of_match_ptr(stm32_romem_of_match),
  	},
  };
  module_platform_driver(stm32_romem_driver);
  
  MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
  MODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM");
  MODULE_ALIAS("platform:nvmem-stm32-romem");
  MODULE_LICENSE("GPL v2");