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