1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
6 #include <linux/bcm47xx_nvram.h>
7 #include <linux/etherdevice.h>
8 #include <linux/if_ether.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/module.h>
12 #include <linux/nvmem-consumer.h>
13 #include <linux/nvmem-provider.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
18 #define NVRAM_MAGIC "FLSH"
21 * struct brcm_nvram - driver state internal struct
23 * @dev: NVMEM device pointer
24 * @nvmem_size: Size of the whole space available for NVRAM
25 * @data: NVRAM data copy stored to avoid poking underlying flash controller
26 * @data_len: NVRAM data size
27 * @padding_byte: Padding value used to fill remaining space
28 * @cells: Array of discovered NVMEM cells
29 * @ncells: Number of elements in cells
37 struct nvmem_cell_info
*cells
;
41 struct brcm_nvram_header
{
44 __le32 crc_ver_init
; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
45 __le32 config_refresh
; /* 0:15 sdram_config, 16:31 sdram_refresh */
46 __le32 config_ncdl
; /* ncdl values for memc */
49 static int brcm_nvram_read(void *context
, unsigned int offset
, void *val
,
52 struct brcm_nvram
*priv
= context
;
55 if (offset
+ bytes
> priv
->data_len
)
56 to_copy
= max_t(ssize_t
, (ssize_t
)priv
->data_len
- offset
, 0);
60 memcpy(val
, priv
->data
+ offset
, to_copy
);
62 memset((uint8_t *)val
+ to_copy
, priv
->padding_byte
, bytes
- to_copy
);
67 static int brcm_nvram_copy_data(struct brcm_nvram
*priv
, struct platform_device
*pdev
)
72 base
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
76 priv
->nvmem_size
= resource_size(res
);
78 priv
->padding_byte
= readb(base
+ priv
->nvmem_size
- 1);
79 for (priv
->data_len
= priv
->nvmem_size
;
82 if (readb(base
+ priv
->data_len
- 1) != priv
->padding_byte
)
85 WARN(priv
->data_len
> SZ_128K
, "Unexpected (big) NVRAM size: %zu B\n", priv
->data_len
);
87 priv
->data
= devm_kzalloc(priv
->dev
, priv
->data_len
, GFP_KERNEL
);
91 memcpy_fromio(priv
->data
, base
, priv
->data_len
);
93 bcm47xx_nvram_init_from_iomem(base
, priv
->data_len
);
98 static int brcm_nvram_read_post_process_macaddr(void *context
, const char *id
, int index
,
99 unsigned int offset
, void *buf
, size_t bytes
)
103 if (bytes
!= 3 * ETH_ALEN
- 1)
106 if (!mac_pton(buf
, mac
))
110 eth_addr_add(mac
, index
);
112 ether_addr_copy(buf
, mac
);
117 static int brcm_nvram_add_cells(struct brcm_nvram
*priv
, uint8_t *data
,
120 struct device
*dev
= priv
->dev
;
126 tmp
= priv
->data
[len
- 1];
127 priv
->data
[len
- 1] = '\0';
130 for (var
= data
+ sizeof(struct brcm_nvram_header
);
131 var
< (char *)data
+ len
&& *var
;
132 var
+= strlen(var
) + 1) {
136 priv
->cells
= devm_kcalloc(dev
, priv
->ncells
, sizeof(*priv
->cells
), GFP_KERNEL
);
142 for (var
= data
+ sizeof(struct brcm_nvram_header
), idx
= 0;
143 var
< (char *)data
+ len
&& *var
;
144 var
= value
+ strlen(value
) + 1, idx
++) {
147 eq
= strchr(var
, '=');
151 name
= devm_kstrdup(dev
, var
, GFP_KERNEL
);
159 priv
->cells
[idx
].name
= name
;
160 priv
->cells
[idx
].offset
= value
- (char *)data
;
161 priv
->cells
[idx
].bytes
= strlen(value
);
162 priv
->cells
[idx
].np
= of_get_child_by_name(dev
->of_node
, priv
->cells
[idx
].name
);
163 if (!strcmp(name
, "et0macaddr") ||
164 !strcmp(name
, "et1macaddr") ||
165 !strcmp(name
, "et2macaddr")) {
166 priv
->cells
[idx
].raw_len
= strlen(value
);
167 priv
->cells
[idx
].bytes
= ETH_ALEN
;
168 priv
->cells
[idx
].read_post_process
= brcm_nvram_read_post_process_macaddr
;
173 priv
->data
[len
- 1] = tmp
;
177 static int brcm_nvram_parse(struct brcm_nvram
*priv
)
179 struct brcm_nvram_header
*header
= (struct brcm_nvram_header
*)priv
->data
;
180 struct device
*dev
= priv
->dev
;
184 if (memcmp(header
->magic
, NVRAM_MAGIC
, 4)) {
185 dev_err(dev
, "Invalid NVRAM magic\n");
189 len
= le32_to_cpu(header
->len
);
190 if (len
> priv
->nvmem_size
) {
191 dev_err(dev
, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len
,
196 err
= brcm_nvram_add_cells(priv
, priv
->data
, len
);
198 dev_err(dev
, "Failed to add cells: %d\n", err
);
203 static int brcm_nvram_probe(struct platform_device
*pdev
)
205 struct nvmem_config config
= {
206 .name
= "brcm-nvram",
207 .reg_read
= brcm_nvram_read
,
209 struct device
*dev
= &pdev
->dev
;
210 struct brcm_nvram
*priv
;
213 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
218 err
= brcm_nvram_copy_data(priv
, pdev
);
222 err
= brcm_nvram_parse(priv
);
227 config
.cells
= priv
->cells
;
228 config
.ncells
= priv
->ncells
;
230 config
.size
= priv
->nvmem_size
;
232 return PTR_ERR_OR_ZERO(devm_nvmem_register(dev
, &config
));
235 static const struct of_device_id brcm_nvram_of_match_table
[] = {
236 { .compatible
= "brcm,nvram", },
240 static struct platform_driver brcm_nvram_driver
= {
241 .probe
= brcm_nvram_probe
,
243 .name
= "brcm_nvram",
244 .of_match_table
= brcm_nvram_of_match_table
,
248 static int __init
brcm_nvram_init(void)
250 return platform_driver_register(&brcm_nvram_driver
);
253 subsys_initcall_sync(brcm_nvram_init
);
255 MODULE_AUTHOR("Rafał Miłecki");
256 MODULE_DESCRIPTION("Broadcom I/O-mapped NVRAM support driver");
257 MODULE_LICENSE("GPL");
258 MODULE_DEVICE_TABLE(of
, brcm_nvram_of_match_table
);