linux/drivers/soc/fsl/qe/qe_common.c
<<
>>
Prefs
   1/*
   2 * Common CPM code
   3 *
   4 * Author: Scott Wood <scottwood@freescale.com>
   5 *
   6 * Copyright 2007-2008,2010 Freescale Semiconductor, Inc.
   7 *
   8 * Some parts derived from commproc.c/cpm2_common.c, which is:
   9 * Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
  10 * Copyright (c) 1999-2001 Dan Malek <dan@embeddedalley.com>
  11 * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com)
  12 * 2006 (c) MontaVista Software, Inc.
  13 * Vitaly Bordug <vbordug@ru.mvista.com>
  14 *
  15 * This program is free software; you can redistribute it and/or modify
  16 * it under the terms of version 2 of the GNU General Public License as
  17 * published by the Free Software Foundation.
  18 */
  19#include <linux/genalloc.h>
  20#include <linux/init.h>
  21#include <linux/list.h>
  22#include <linux/of_device.h>
  23#include <linux/spinlock.h>
  24#include <linux/export.h>
  25#include <linux/of.h>
  26#include <linux/of_address.h>
  27#include <linux/slab.h>
  28#include <linux/io.h>
  29#include <soc/fsl/qe/qe.h>
  30
  31static struct gen_pool *muram_pool;
  32static spinlock_t cpm_muram_lock;
  33static u8 __iomem *muram_vbase;
  34static phys_addr_t muram_pbase;
  35
  36struct muram_block {
  37        struct list_head head;
  38        unsigned long start;
  39        int size;
  40};
  41
  42static LIST_HEAD(muram_block_list);
  43
  44/* max address size we deal with */
  45#define OF_MAX_ADDR_CELLS       4
  46#define GENPOOL_OFFSET          (4096 * 8)
  47
  48int cpm_muram_init(void)
  49{
  50        struct device_node *np;
  51        struct resource r;
  52        u32 zero[OF_MAX_ADDR_CELLS] = {};
  53        resource_size_t max = 0;
  54        int i = 0;
  55        int ret = 0;
  56
  57        if (muram_pbase)
  58                return 0;
  59
  60        spin_lock_init(&cpm_muram_lock);
  61        np = of_find_compatible_node(NULL, NULL, "fsl,cpm-muram-data");
  62        if (!np) {
  63                /* try legacy bindings */
  64                np = of_find_node_by_name(NULL, "data-only");
  65                if (!np) {
  66                        pr_err("Cannot find CPM muram data node");
  67                        ret = -ENODEV;
  68                        goto out_muram;
  69                }
  70        }
  71
  72        muram_pool = gen_pool_create(0, -1);
  73        muram_pbase = of_translate_address(np, zero);
  74        if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) {
  75                pr_err("Cannot translate zero through CPM muram node");
  76                ret = -ENODEV;
  77                goto out_pool;
  78        }
  79
  80        while (of_address_to_resource(np, i++, &r) == 0) {
  81                if (r.end > max)
  82                        max = r.end;
  83                ret = gen_pool_add(muram_pool, r.start - muram_pbase +
  84                                   GENPOOL_OFFSET, resource_size(&r), -1);
  85                if (ret) {
  86                        pr_err("QE: couldn't add muram to pool!\n");
  87                        goto out_pool;
  88                }
  89        }
  90
  91        muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1);
  92        if (!muram_vbase) {
  93                pr_err("Cannot map QE muram");
  94                ret = -ENOMEM;
  95                goto out_pool;
  96        }
  97        goto out_muram;
  98out_pool:
  99        gen_pool_destroy(muram_pool);
 100out_muram:
 101        of_node_put(np);
 102        return ret;
 103}
 104
 105/*
 106 * cpm_muram_alloc_common - cpm_muram_alloc common code
 107 * @size: number of bytes to allocate
 108 * @algo: algorithm for alloc.
 109 * @data: data for genalloc's algorithm.
 110 *
 111 * This function returns an offset into the muram area.
 112 */
 113static unsigned long cpm_muram_alloc_common(unsigned long size,
 114                genpool_algo_t algo, void *data)
 115{
 116        struct muram_block *entry;
 117        unsigned long start;
 118
 119        start = gen_pool_alloc_algo(muram_pool, size, algo, data);
 120        if (!start)
 121                goto out2;
 122        start = start - GENPOOL_OFFSET;
 123        memset_io(cpm_muram_addr(start), 0, size);
 124        entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
 125        if (!entry)
 126                goto out1;
 127        entry->start = start;
 128        entry->size = size;
 129        list_add(&entry->head, &muram_block_list);
 130
 131        return start;
 132out1:
 133        gen_pool_free(muram_pool, start, size);
 134out2:
 135        return (unsigned long)-ENOMEM;
 136}
 137
 138/*
 139 * cpm_muram_alloc - allocate the requested size worth of multi-user ram
 140 * @size: number of bytes to allocate
 141 * @align: requested alignment, in bytes
 142 *
 143 * This function returns an offset into the muram area.
 144 * Use cpm_dpram_addr() to get the virtual address of the area.
 145 * Use cpm_muram_free() to free the allocation.
 146 */
 147unsigned long cpm_muram_alloc(unsigned long size, unsigned long align)
 148{
 149        unsigned long start;
 150        unsigned long flags;
 151        struct genpool_data_align muram_pool_data;
 152
 153        spin_lock_irqsave(&cpm_muram_lock, flags);
 154        muram_pool_data.align = align;
 155        start = cpm_muram_alloc_common(size, gen_pool_first_fit_align,
 156                                       &muram_pool_data);
 157        spin_unlock_irqrestore(&cpm_muram_lock, flags);
 158        return start;
 159}
 160EXPORT_SYMBOL(cpm_muram_alloc);
 161
 162/**
 163 * cpm_muram_free - free a chunk of multi-user ram
 164 * @offset: The beginning of the chunk as returned by cpm_muram_alloc().
 165 */
 166int cpm_muram_free(unsigned long offset)
 167{
 168        unsigned long flags;
 169        int size;
 170        struct muram_block *tmp;
 171
 172        size = 0;
 173        spin_lock_irqsave(&cpm_muram_lock, flags);
 174        list_for_each_entry(tmp, &muram_block_list, head) {
 175                if (tmp->start == offset) {
 176                        size = tmp->size;
 177                        list_del(&tmp->head);
 178                        kfree(tmp);
 179                        break;
 180                }
 181        }
 182        gen_pool_free(muram_pool, offset + GENPOOL_OFFSET, size);
 183        spin_unlock_irqrestore(&cpm_muram_lock, flags);
 184        return size;
 185}
 186EXPORT_SYMBOL(cpm_muram_free);
 187
 188/*
 189 * cpm_muram_alloc_fixed - reserve a specific region of multi-user ram
 190 * @offset: offset of allocation start address
 191 * @size: number of bytes to allocate
 192 * This function returns an offset into the muram area
 193 * Use cpm_dpram_addr() to get the virtual address of the area.
 194 * Use cpm_muram_free() to free the allocation.
 195 */
 196unsigned long cpm_muram_alloc_fixed(unsigned long offset, unsigned long size)
 197{
 198        unsigned long start;
 199        unsigned long flags;
 200        struct genpool_data_fixed muram_pool_data_fixed;
 201
 202        spin_lock_irqsave(&cpm_muram_lock, flags);
 203        muram_pool_data_fixed.offset = offset + GENPOOL_OFFSET;
 204        start = cpm_muram_alloc_common(size, gen_pool_fixed_alloc,
 205                                       &muram_pool_data_fixed);
 206        spin_unlock_irqrestore(&cpm_muram_lock, flags);
 207        return start;
 208}
 209EXPORT_SYMBOL(cpm_muram_alloc_fixed);
 210
 211/**
 212 * cpm_muram_addr - turn a muram offset into a virtual address
 213 * @offset: muram offset to convert
 214 */
 215void __iomem *cpm_muram_addr(unsigned long offset)
 216{
 217        return muram_vbase + offset;
 218}
 219EXPORT_SYMBOL(cpm_muram_addr);
 220
 221unsigned long cpm_muram_offset(void __iomem *addr)
 222{
 223        return addr - (void __iomem *)muram_vbase;
 224}
 225EXPORT_SYMBOL(cpm_muram_offset);
 226
 227/**
 228 * cpm_muram_dma - turn a muram virtual address into a DMA address
 229 * @offset: virtual address from cpm_muram_addr() to convert
 230 */
 231dma_addr_t cpm_muram_dma(void __iomem *addr)
 232{
 233        return muram_pbase + ((u8 __iomem *)addr - muram_vbase);
 234}
 235EXPORT_SYMBOL(cpm_muram_dma);
 236