bcm53xx: move NVRAM driver to the target files
[15.05/openwrt.git] / target / linux / bcm53xx / files / drivers / misc / bcm47xx-nvram.c
1 /*
2  * BCM947xx nvram variable access
3  *
4  * Copyright (C) 2005 Broadcom Corporation
5  * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
6  * Copyright (C) 2010-2014 Hauke Mehrtens <hauke@hauke-m.de>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13
14 #include <linux/types.h>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/of_address.h>
19 #include <linux/device.h>
20 #include <linux/platform_device.h>
21 #include <linux/io.h>
22 #include <linux/bcm47xx_nvram.h>
23
24 struct bcm47xx_nvram {
25         size_t nvram_len;
26         char *nvram_buf;
27 };
28
29 static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
30
31 static u32 find_nvram_size(void __iomem *end)
32 {
33         struct nvram_header __iomem *header;
34         int i;
35
36         for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
37                 header = (struct nvram_header __iomem *)(end - nvram_sizes[i]);
38                 if (__raw_readl(&header->magic) == NVRAM_HEADER)
39                         return nvram_sizes[i];
40         }
41
42         return 0;
43 }
44
45 /* Probe for NVRAM header */
46 static int nvram_find_and_copy(struct device *dev, void __iomem *base,
47                                size_t len, char **nvram_buf,
48                                size_t *nvram_len)
49 {
50         struct nvram_header __iomem *header;
51         int i;
52         u32 off;
53         u32 *dst;
54         __le32 __iomem *src;
55         u32 size;
56
57         /* TODO: when nvram is on nand flash check for bad blocks first. */
58         off = FLASH_MIN;
59         while (off <= len) {
60                 /* Windowed flash access */
61                 size = find_nvram_size(base + off);
62                 if (size) {
63                         header = (struct nvram_header __iomem *)
64                                         (base + off - size);
65                         goto found;
66                 }
67                 off += 0x10000;
68         }
69
70         /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
71         header = (struct nvram_header __iomem *)(base + 4096);
72         if (__raw_readl(&header->magic) == NVRAM_HEADER) {
73                 size = NVRAM_SPACE;
74                 goto found;
75         }
76
77         header = (struct nvram_header __iomem *)(base + 1024);
78         if (__raw_readl(&header->magic) == NVRAM_HEADER) {
79                 size = NVRAM_SPACE;
80                 goto found;
81         }
82
83         *nvram_buf = NULL;
84         *nvram_len = 0;
85         pr_err("no nvram found\n");
86         return -ENXIO;
87
88 found:
89         if (readl(&header->len) > size)
90                 pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n");
91         *nvram_len = min_t(u32, readl(&header->len), size);
92
93         *nvram_buf = devm_kzalloc(dev, *nvram_len, GFP_KERNEL);
94         if (!*nvram_buf)
95                 return -ENOMEM;
96
97         src = (__le32 __iomem *) header;
98         dst = (u32 *) *nvram_buf;
99         for (i = 0; i < sizeof(struct nvram_header); i += 4)
100                 *dst++ = __raw_readl(src++);
101         for (; i < *nvram_len; i += 4)
102                 *dst++ = readl(src++);
103
104         return 0;
105 }
106
107 int bcm47xx_nvram_getenv(const struct device *dev, const char *name, char *val,
108                          size_t val_len)
109 {
110         char *var, *value, *end, *eq;
111         struct bcm47xx_nvram *nvram;
112
113         if (!dev)
114                 return -ENODEV;
115
116         nvram = dev_get_drvdata(dev);
117
118         if (!name || !nvram || !nvram->nvram_len)
119                 return -EINVAL;
120
121         /* Look for name=value and return value */
122         var = nvram->nvram_buf + sizeof(struct nvram_header);
123         end = nvram->nvram_buf + nvram->nvram_len - 2;
124         end[0] = end[1] = '\0';
125         for (; *var; var = value + strlen(value) + 1) {
126                 eq = strchr(var, '=');
127                 if (!eq)
128                         break;
129                 value = eq + 1;
130                 if ((eq - var) == strlen(name) &&
131                         strncmp(var, name, (eq - var)) == 0) {
132                         return snprintf(val, val_len, "%s", value);
133                 }
134         }
135         return -ENOENT;
136 }
137 EXPORT_SYMBOL(bcm47xx_nvram_getenv);
138
139 int bcm47xx_nvram_gpio_pin(const struct device *dev, const char *name)
140 {
141         int i, err;
142         char nvram_var[10];
143         char buf[30];
144
145         if (!dev)
146                 return -ENODEV;
147
148         for (i = 0; i < 32; i++) {
149                 err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
150                 if (err <= 0)
151                         continue;
152                 err = bcm47xx_nvram_getenv(dev, nvram_var, buf, sizeof(buf));
153                 if (err <= 0)
154                         continue;
155                 if (!strcmp(name, buf))
156                         return i;
157         }
158         return -ENOENT;
159 }
160 EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
161
162 static int bcm47xx_nvram_probe(struct platform_device *pdev)
163 {
164         struct device *dev = &pdev->dev;
165         struct device_node *np = dev->of_node;
166         struct bcm47xx_nvram *nvram;
167         int err;
168         struct resource flash_mem;
169         void __iomem *mmio;
170
171         /* Alloc */
172         nvram = devm_kzalloc(dev, sizeof(*nvram), GFP_KERNEL);
173         if (!nvram)
174                 return -ENOMEM;
175
176         err = of_address_to_resource(np, 0, &flash_mem);
177         if (err)
178                 return err;
179
180         mmio = ioremap_nocache(flash_mem.start, resource_size(&flash_mem));
181         if (!mmio)
182                 return -ENOMEM;
183
184         err = nvram_find_and_copy(dev, mmio, resource_size(&flash_mem),
185                                   &nvram->nvram_buf, &nvram->nvram_len);
186         if (err)
187                 goto err_unmap_mmio;
188
189         platform_set_drvdata(pdev, nvram);
190
191 err_unmap_mmio:
192         iounmap(mmio);
193         return err;
194 }
195
196 static const struct of_device_id bcm47xx_nvram_of_match_table[] = {
197         { .compatible = "brcm,bcm47xx-nvram", },
198         {},
199 };
200 MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
201
202 static struct platform_driver bcm47xx_nvram_driver = {
203         .driver = {
204                 .owner = THIS_MODULE,
205                 .name = "bcm47xx-nvram",
206                 .of_match_table = bcm47xx_nvram_of_match_table,
207                 /* driver unloading/unbinding currently not supported */
208                 .suppress_bind_attrs = true,
209         },
210         .probe = bcm47xx_nvram_probe,
211 };
212 module_platform_driver(bcm47xx_nvram_driver);
213
214 MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
215 MODULE_LICENSE("GPLv2");