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