123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Common CPM code
- *
- * Author: Scott Wood <[email protected]>
- *
- * Copyright 2007-2008,2010 Freescale Semiconductor, Inc.
- *
- * Some parts derived from commproc.c/cpm2_common.c, which is:
- * Copyright (c) 1997 Dan error_act ([email protected])
- * Copyright (c) 1999-2001 Dan Malek <[email protected]>
- * Copyright (c) 2000 MontaVista Software, Inc ([email protected])
- * 2006 (c) MontaVista Software, Inc.
- * Vitaly Bordug <[email protected]>
- */
- #include <linux/genalloc.h>
- #include <linux/init.h>
- #include <linux/list.h>
- #include <linux/of_device.h>
- #include <linux/spinlock.h>
- #include <linux/export.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/slab.h>
- #include <linux/io.h>
- #include <soc/fsl/qe/qe.h>
- static struct gen_pool *muram_pool;
- static DEFINE_SPINLOCK(cpm_muram_lock);
- static void __iomem *muram_vbase;
- static phys_addr_t muram_pbase;
- struct muram_block {
- struct list_head head;
- s32 start;
- int size;
- };
- static LIST_HEAD(muram_block_list);
- /* max address size we deal with */
- #define OF_MAX_ADDR_CELLS 4
- #define GENPOOL_OFFSET (4096 * 8)
- int cpm_muram_init(void)
- {
- struct device_node *np;
- struct resource r;
- __be32 zero[OF_MAX_ADDR_CELLS] = {};
- resource_size_t max = 0;
- int i = 0;
- int ret = 0;
- if (muram_pbase)
- return 0;
- np = of_find_compatible_node(NULL, NULL, "fsl,cpm-muram-data");
- if (!np) {
- /* try legacy bindings */
- np = of_find_node_by_name(NULL, "data-only");
- if (!np) {
- pr_err("Cannot find CPM muram data node");
- ret = -ENODEV;
- goto out_muram;
- }
- }
- muram_pool = gen_pool_create(0, -1);
- if (!muram_pool) {
- pr_err("Cannot allocate memory pool for CPM/QE muram");
- ret = -ENOMEM;
- goto out_muram;
- }
- muram_pbase = of_translate_address(np, zero);
- if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) {
- pr_err("Cannot translate zero through CPM muram node");
- ret = -ENODEV;
- goto out_pool;
- }
- while (of_address_to_resource(np, i++, &r) == 0) {
- if (r.end > max)
- max = r.end;
- ret = gen_pool_add(muram_pool, r.start - muram_pbase +
- GENPOOL_OFFSET, resource_size(&r), -1);
- if (ret) {
- pr_err("QE: couldn't add muram to pool!\n");
- goto out_pool;
- }
- }
- muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1);
- if (!muram_vbase) {
- pr_err("Cannot map QE muram");
- ret = -ENOMEM;
- goto out_pool;
- }
- goto out_muram;
- out_pool:
- gen_pool_destroy(muram_pool);
- out_muram:
- of_node_put(np);
- return ret;
- }
- /*
- * cpm_muram_alloc_common - cpm_muram_alloc common code
- * @size: number of bytes to allocate
- * @algo: algorithm for alloc.
- * @data: data for genalloc's algorithm.
- *
- * This function returns a non-negative offset into the muram area, or
- * a negative errno on failure.
- */
- static s32 cpm_muram_alloc_common(unsigned long size,
- genpool_algo_t algo, void *data)
- {
- struct muram_block *entry;
- s32 start;
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (!entry)
- return -ENOMEM;
- start = gen_pool_alloc_algo(muram_pool, size, algo, data);
- if (!start) {
- kfree(entry);
- return -ENOMEM;
- }
- start = start - GENPOOL_OFFSET;
- memset_io(cpm_muram_addr(start), 0, size);
- entry->start = start;
- entry->size = size;
- list_add(&entry->head, &muram_block_list);
- return start;
- }
- /*
- * cpm_muram_alloc - allocate the requested size worth of multi-user ram
- * @size: number of bytes to allocate
- * @align: requested alignment, in bytes
- *
- * This function returns a non-negative offset into the muram area, or
- * a negative errno on failure.
- * Use cpm_dpram_addr() to get the virtual address of the area.
- * Use cpm_muram_free() to free the allocation.
- */
- s32 cpm_muram_alloc(unsigned long size, unsigned long align)
- {
- s32 start;
- unsigned long flags;
- struct genpool_data_align muram_pool_data;
- spin_lock_irqsave(&cpm_muram_lock, flags);
- muram_pool_data.align = align;
- start = cpm_muram_alloc_common(size, gen_pool_first_fit_align,
- &muram_pool_data);
- spin_unlock_irqrestore(&cpm_muram_lock, flags);
- return start;
- }
- EXPORT_SYMBOL(cpm_muram_alloc);
- /**
- * cpm_muram_free - free a chunk of multi-user ram
- * @offset: The beginning of the chunk as returned by cpm_muram_alloc().
- */
- void cpm_muram_free(s32 offset)
- {
- unsigned long flags;
- int size;
- struct muram_block *tmp;
- if (offset < 0)
- return;
- size = 0;
- spin_lock_irqsave(&cpm_muram_lock, flags);
- list_for_each_entry(tmp, &muram_block_list, head) {
- if (tmp->start == offset) {
- size = tmp->size;
- list_del(&tmp->head);
- kfree(tmp);
- break;
- }
- }
- gen_pool_free(muram_pool, offset + GENPOOL_OFFSET, size);
- spin_unlock_irqrestore(&cpm_muram_lock, flags);
- }
- EXPORT_SYMBOL(cpm_muram_free);
- /*
- * cpm_muram_alloc_fixed - reserve a specific region of multi-user ram
- * @offset: offset of allocation start address
- * @size: number of bytes to allocate
- * This function returns @offset if the area was available, a negative
- * errno otherwise.
- * Use cpm_dpram_addr() to get the virtual address of the area.
- * Use cpm_muram_free() to free the allocation.
- */
- s32 cpm_muram_alloc_fixed(unsigned long offset, unsigned long size)
- {
- s32 start;
- unsigned long flags;
- struct genpool_data_fixed muram_pool_data_fixed;
- spin_lock_irqsave(&cpm_muram_lock, flags);
- muram_pool_data_fixed.offset = offset + GENPOOL_OFFSET;
- start = cpm_muram_alloc_common(size, gen_pool_fixed_alloc,
- &muram_pool_data_fixed);
- spin_unlock_irqrestore(&cpm_muram_lock, flags);
- return start;
- }
- EXPORT_SYMBOL(cpm_muram_alloc_fixed);
- /**
- * cpm_muram_addr - turn a muram offset into a virtual address
- * @offset: muram offset to convert
- */
- void __iomem *cpm_muram_addr(unsigned long offset)
- {
- return muram_vbase + offset;
- }
- EXPORT_SYMBOL(cpm_muram_addr);
- unsigned long cpm_muram_offset(const void __iomem *addr)
- {
- return addr - muram_vbase;
- }
- EXPORT_SYMBOL(cpm_muram_offset);
- /**
- * cpm_muram_dma - turn a muram virtual address into a DMA address
- * @addr: virtual address from cpm_muram_addr() to convert
- */
- dma_addr_t cpm_muram_dma(void __iomem *addr)
- {
- return muram_pbase + (addr - muram_vbase);
- }
- EXPORT_SYMBOL(cpm_muram_dma);
- /*
- * As cpm_muram_free, but takes the virtual address rather than the
- * muram offset.
- */
- void cpm_muram_free_addr(const void __iomem *addr)
- {
- if (!addr)
- return;
- cpm_muram_free(cpm_muram_offset(addr));
- }
- EXPORT_SYMBOL(cpm_muram_free_addr);
|