Merge tag 'm68k-for-v5.3-tag2' of git://git.kernel.org/pub/scm/linux/kernel/git/geert...
[sfrench/cifs-2.6.git] / arch / arm / mach-netx / xc.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * arch/arm/mach-netx/xc.c
4  *
5  * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
6  */
7
8 #include <linux/init.h>
9 #include <linux/device.h>
10 #include <linux/firmware.h>
11 #include <linux/mutex.h>
12 #include <linux/slab.h>
13 #include <linux/io.h>
14 #include <linux/export.h>
15
16 #include <mach/hardware.h>
17 #include <mach/irqs.h>
18 #include <mach/netx-regs.h>
19
20 #include <mach/xc.h>
21
22 static DEFINE_MUTEX(xc_lock);
23
24 static int xc_in_use = 0;
25
26 struct fw_desc {
27         unsigned int ofs;
28         unsigned int size;
29         unsigned int patch_ofs;
30         unsigned int patch_entries;
31 };
32
33 struct fw_header {
34         unsigned int magic;
35         unsigned int type;
36         unsigned int version;
37         unsigned int reserved[5];
38         struct fw_desc fw_desc[3];
39 } __attribute__ ((packed));
40
41 int xc_stop(struct xc *x)
42 {
43         writel(RPU_HOLD_PC, x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS);
44         writel(TPU_HOLD_PC, x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS);
45         writel(XPU_HOLD_PC, x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS);
46         return 0;
47 }
48
49 int xc_start(struct xc *x)
50 {
51         writel(0, x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS);
52         writel(0, x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS);
53         writel(0, x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS);
54         return 0;
55 }
56
57 int xc_running(struct xc *x)
58 {
59         return (readl(x->xmac_base + NETX_XMAC_RPU_HOLD_PC_OFS) & RPU_HOLD_PC)
60             || (readl(x->xmac_base + NETX_XMAC_TPU_HOLD_PC_OFS) & TPU_HOLD_PC)
61             || (readl(x->xpec_base + NETX_XPEC_XPU_HOLD_PC_OFS) & XPU_HOLD_PC) ?
62                 0 : 1;
63 }
64
65 int xc_reset(struct xc *x)
66 {
67         writel(0, x->xpec_base + NETX_XPEC_PC_OFS);
68         return 0;
69 }
70
71 static int xc_check_ptr(struct xc *x, unsigned long adr, unsigned int size)
72 {
73         if (adr >= NETX_PA_XMAC(x->no) &&
74             adr + size < NETX_PA_XMAC(x->no) + XMAC_MEM_SIZE)
75                 return 0;
76
77         if (adr >= NETX_PA_XPEC(x->no) &&
78             adr + size < NETX_PA_XPEC(x->no) + XPEC_MEM_SIZE)
79                 return 0;
80
81         dev_err(x->dev, "Illegal pointer in firmware found. aborting\n");
82
83         return -1;
84 }
85
86 static int xc_patch(struct xc *x, const void *patch, int count)
87 {
88         unsigned int val, adr;
89         const unsigned int *data = patch;
90
91         int i;
92         for (i = 0; i < count; i++) {
93                 adr = *data++;
94                 val = *data++;
95                 if (xc_check_ptr(x, adr, 4) < 0)
96                         return -EINVAL;
97
98                 writel(val, (void __iomem *)io_p2v(adr));
99         }
100         return 0;
101 }
102
103 int xc_request_firmware(struct xc *x)
104 {
105         int ret;
106         char name[16];
107         const struct firmware *fw;
108         struct fw_header *head;
109         unsigned int size;
110         int i;
111         const void *src;
112         unsigned long dst;
113
114         sprintf(name, "xc%d.bin", x->no);
115
116         ret = request_firmware(&fw, name, x->dev);
117
118         if (ret < 0) {
119                 dev_err(x->dev, "request_firmware failed\n");
120                 return ret;
121         }
122
123         head = (struct fw_header *)fw->data;
124         if (head->magic != 0x4e657458) {
125                 if (head->magic == 0x5874654e) {
126                         dev_err(x->dev,
127                             "firmware magic is 'XteN'. Endianness problems?\n");
128                         ret = -ENODEV;
129                         goto exit_release_firmware;
130                 }
131                 dev_err(x->dev, "unrecognized firmware magic 0x%08x\n",
132                         head->magic);
133                 ret = -ENODEV;
134                 goto exit_release_firmware;
135         }
136
137         x->type = head->type;
138         x->version = head->version;
139
140         ret = -EINVAL;
141
142         for (i = 0; i < 3; i++) {
143                 src = fw->data + head->fw_desc[i].ofs;
144                 dst = *(unsigned int *)src;
145                 src += sizeof (unsigned int);
146                 size = head->fw_desc[i].size - sizeof (unsigned int);
147
148                 if (xc_check_ptr(x, dst, size))
149                         goto exit_release_firmware;
150
151                 memcpy((void *)io_p2v(dst), src, size);
152
153                 src = fw->data + head->fw_desc[i].patch_ofs;
154                 size = head->fw_desc[i].patch_entries;
155                 ret = xc_patch(x, src, size);
156                 if (ret < 0)
157                         goto exit_release_firmware;
158         }
159
160         ret = 0;
161
162       exit_release_firmware:
163         release_firmware(fw);
164
165         return ret;
166 }
167
168 struct xc *request_xc(int xcno, struct device *dev)
169 {
170         struct xc *x = NULL;
171
172         mutex_lock(&xc_lock);
173
174         if (xcno > 3)
175                 goto exit;
176         if (xc_in_use & (1 << xcno))
177                 goto exit;
178
179         x = kmalloc(sizeof (struct xc), GFP_KERNEL);
180         if (!x)
181                 goto exit;
182
183         if (!request_mem_region
184             (NETX_PA_XPEC(xcno), XPEC_MEM_SIZE, kobject_name(&dev->kobj)))
185                 goto exit_free;
186
187         if (!request_mem_region
188             (NETX_PA_XMAC(xcno), XMAC_MEM_SIZE, kobject_name(&dev->kobj)))
189                 goto exit_release_1;
190
191         if (!request_mem_region
192             (SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE, kobject_name(&dev->kobj)))
193                 goto exit_release_2;
194
195         x->xpec_base = (void * __iomem)io_p2v(NETX_PA_XPEC(xcno));
196         x->xmac_base = (void * __iomem)io_p2v(NETX_PA_XMAC(xcno));
197         x->sram_base = ioremap(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
198         if (!x->sram_base)
199                 goto exit_release_3;
200
201         x->irq = NETX_IRQ_XPEC(xcno);
202
203         x->no = xcno;
204         x->dev = dev;
205
206         xc_in_use |= (1 << xcno);
207
208         goto exit;
209
210       exit_release_3:
211         release_mem_region(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
212       exit_release_2:
213         release_mem_region(NETX_PA_XMAC(xcno), XMAC_MEM_SIZE);
214       exit_release_1:
215         release_mem_region(NETX_PA_XPEC(xcno), XPEC_MEM_SIZE);
216       exit_free:
217         kfree(x);
218         x = NULL;
219       exit:
220         mutex_unlock(&xc_lock);
221         return x;
222 }
223
224 void free_xc(struct xc *x)
225 {
226         int xcno = x->no;
227
228         mutex_lock(&xc_lock);
229
230         iounmap(x->sram_base);
231         release_mem_region(SRAM_INTERNAL_PHYS(xcno), SRAM_MEM_SIZE);
232         release_mem_region(NETX_PA_XMAC(xcno), XMAC_MEM_SIZE);
233         release_mem_region(NETX_PA_XPEC(xcno), XPEC_MEM_SIZE);
234         xc_in_use &= ~(1 << x->no);
235         kfree(x);
236
237         mutex_unlock(&xc_lock);
238 }
239
240 EXPORT_SYMBOL(free_xc);
241 EXPORT_SYMBOL(request_xc);
242 EXPORT_SYMBOL(xc_request_firmware);
243 EXPORT_SYMBOL(xc_reset);
244 EXPORT_SYMBOL(xc_running);
245 EXPORT_SYMBOL(xc_start);
246 EXPORT_SYMBOL(xc_stop);