*
* Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
*
+ * Conversion to mempool API and ARM MMU section mapping
+ * by Paul Mundt <paul.mundt@nokia.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
#include <linux/fb.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/mempool.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/uaccess.h>
static unsigned long dsp_fault_adr;
static struct mem_sync_struct mem_sync;
+static void *mempool_alloc_from_pool(mempool_t *pool,
+ unsigned int __nocast gfp_mask)
+{
+ spin_lock_irq(&pool->lock);
+ if (likely(pool->curr_nr)) {
+ void *element = pool->elements[--pool->curr_nr];
+ spin_unlock_irq(&pool->lock);
+ return element;
+ }
+
+ spin_unlock_irq(&pool->lock);
+ return mempool_alloc(pool, gfp_mask);
+}
+
static __inline__ unsigned long lineup_offset(unsigned long adr,
unsigned long ref,
unsigned long mask)
return 0;
}
-/*
- * kmem_reserve(), kmem_release():
- * reserve or release kernel memory for exmap().
- *
- * exmap() might request consecutive 1MB or 64kB,
- * but it will be difficult after memory pages are fragmented.
- * So, user can reserve such memory blocks in the early phase
- * through kmem_reserve().
- */
-struct kmem_pool {
- struct semaphore sem;
- unsigned long buf[16];
- int count;
-};
+static mempool_t *kmem_pool_1M;
+static mempool_t *kmem_pool_64K;
-#define KMEM_POOL_INIT(name) \
-{ \
- .sem = __SEMAPHORE_INIT((name).sem, 1), \
+static void *dsp_pool_alloc(unsigned int __nocast gfp, void *order)
+{
+ return (void *)__get_dma_pages(gfp, (unsigned int)order);
}
-#define DECLARE_KMEM_POOL(name) \
- struct kmem_pool name = KMEM_POOL_INIT(name)
-DECLARE_KMEM_POOL(kmem_pool_1M);
-DECLARE_KMEM_POOL(kmem_pool_64K);
+static void dsp_pool_free(void *buf, void *order)
+{
+ free_pages((unsigned long)buf, (unsigned int)order);
+}
static void dsp_kmem_release(void)
{
- int i;
-
- down(&kmem_pool_1M.sem);
- for (i = 0; i < kmem_pool_1M.count; i++) {
- if (kmem_pool_1M.buf[i])
- free_pages(kmem_pool_1M.buf[i], ORDER_1MB);
+ if (kmem_pool_64K) {
+ mempool_destroy(kmem_pool_64K);
+ kmem_pool_64K = NULL;
}
- kmem_pool_1M.count = 0;
- up(&kmem_pool_1M.sem);
- down(&kmem_pool_64K.sem);
- for (i = 0; i < kmem_pool_64K.count; i++) {
- if (kmem_pool_64K.buf[i])
- free_pages(kmem_pool_64K.buf[i], ORDER_64KB);
+ if (kmem_pool_1M) {
+ mempool_destroy(kmem_pool_1M);
+ kmem_pool_1M = NULL;
}
- kmem_pool_64K.count = 0;
- up(&kmem_pool_1M.sem);
}
static int dsp_kmem_reserve(unsigned long size)
{
- unsigned long buf;
- unsigned int order;
- unsigned long unit;
- unsigned long _size;
- struct kmem_pool *pool;
- int i;
+ unsigned long len = size;
/* alignment check */
if (!is_aligned(size, SZ_64KB)) {
return -EINVAL;
}
- for (_size = size; _size; _size -= unit) {
- if (_size >= SZ_1MB) {
- unit = SZ_1MB;
- order = ORDER_1MB;
- pool = &kmem_pool_1M;
- } else {
- unit = SZ_64KB;
- order = ORDER_64KB;
- pool = &kmem_pool_64K;
- }
+ if (size >= SZ_1MB) {
+ int nr = size >> 20;
- buf = __get_dma_pages(GFP_KERNEL, order);
- if (!buf)
- return size - _size;
- down(&pool->sem);
- for (i = 0; i < 16; i++) {
- if (!pool->buf[i]) {
- pool->buf[i] = buf;
- pool->count++;
- buf = 0;
- break;
- }
- }
- up(&pool->sem);
+ if (likely(!kmem_pool_1M))
+ kmem_pool_1M = mempool_create(nr,
+ dsp_pool_alloc,
+ dsp_pool_free,
+ (void *)ORDER_1MB);
+ else
+ mempool_resize(kmem_pool_1M, kmem_pool_1M->min_nr + nr,
+ GFP_KERNEL);
- if (buf) { /* pool is full */
- free_pages(buf, order);
- return size - _size;
- }
+ size &= ~(0xf << 20);
}
- return size;
-}
+ if (size >= SZ_64KB) {
+ int nr = size >> 16;
-static unsigned long dsp_mem_get_dma_pages(unsigned int order)
-{
- struct kmem_pool *pool;
- unsigned long buf = 0;
- int i;
+ if (likely(!kmem_pool_64K))
+ kmem_pool_64K = mempool_create(nr,
+ dsp_pool_alloc,
+ dsp_pool_free,
+ (void *)ORDER_64KB);
+ else
+ mempool_resize(kmem_pool_64K,
+ kmem_pool_64K->min_nr + nr, GFP_KERNEL);
- switch (order) {
- case ORDER_1MB:
- pool = &kmem_pool_1M;
- break;
- case ORDER_64KB:
- pool = &kmem_pool_64K;
- break;
- default:
- pool = NULL;
+ size &= ~(0xf << 16);
}
- if (pool) {
- down(&pool->sem);
- for (i = 0; i < pool->count; i++) {
- if (pool->buf[i]) {
- buf = pool->buf[i];
- pool->buf[i] = 0;
- break;
- }
- }
- up(&pool->sem);
- if (buf)
- return buf;
- }
+ if (size)
+ len -= size;
- /* other size or not found in pool */
- return __get_dma_pages(GFP_KERNEL, order);
+ return len;
}
static void dsp_mem_free_pages(unsigned long buf, unsigned int order)
{
- struct kmem_pool *pool;
struct page *page, *ps, *pe;
- int i;
ps = virt_to_page(buf);
pe = virt_to_page(buf + (1 << (PAGE_SHIFT + order)));
- for (page = ps; page < pe; page++) {
+
+ for (page = ps; page < pe; page++)
ClearPageReserved(page);
- }
- /*
- * return buffer to kmem_pool or paging system
- */
- switch (order) {
- case ORDER_1MB:
- pool = &kmem_pool_1M;
- break;
- case ORDER_64KB:
- pool = &kmem_pool_64K;
- break;
- default:
- pool = NULL;
+ if (buf) {
+ if ((order == ORDER_64KB) && likely(kmem_pool_64K))
+ mempool_free((void *)buf, kmem_pool_64K);
+ else if ((order == ORDER_1MB) && likely(kmem_pool_1M))
+ mempool_free((void *)buf, kmem_pool_1M);
+ else
+ free_pages(buf, order);
}
+}
- if (pool) {
- down(&pool->sem);
- for (i = 0; i < pool->count; i++) {
- if (!pool->buf[i]) {
- pool->buf[i] = buf;
- buf = 0;
- }
- }
- up(&pool->sem);
+static inline void
+exmap_alloc_pte(unsigned long virt, unsigned long phys, pgprot_t prot)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ pgd = pgd_offset_k(virt);
+ pud = pud_offset(pgd, virt);
+ pmd = pmd_offset(pud, virt);
+
+ if (pmd_none(*pmd)) {
+ pte = pte_alloc_one_kernel(&init_mm, 0);
+ if (!pte)
+ return;
+
+ /* note: two PMDs will be set */
+ pmd_populate_kernel(&init_mm, pmd, pte);
}
- /* other size or pool is filled */
- if (buf)
- free_pages(buf, order);
+ pte = pte_offset_kernel(pmd, virt);
+ set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot));
+}
+
+static inline int
+exmap_alloc_sect(unsigned long virt, unsigned long phys, int prot)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+
+ pgd = pgd_offset_k(virt);
+ pud = pud_alloc(&init_mm, pgd, virt);
+ pmd = pmd_alloc(&init_mm, pud, virt);
+
+ if (virt & (1 << 20))
+ pmd++;
+
+ if (!pmd_none(*pmd))
+ /* No good, fall back on smaller mappings. */
+ return -EINVAL;
+
+ *pmd = __pmd(phys | prot);
+ flush_pmd_entry(pmd);
+
+ return 0;
}
/*
unsigned long size)
{
long off;
- unsigned long sz_left;
- pmd_t *pmdp;
- pte_t *ptep;
- int prot_pmd, prot_pte;
+ pgprot_t prot_pte;
+ int prot_sect;
printk(KERN_DEBUG
"omapdsp: mapping in ARM MMU, v=0x%08lx, p=0x%08lx, sz=0x%lx\n",
virt, phys, size);
- prot_pmd = PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO);
- prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE;
+ prot_pte = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
+ L_PTE_DIRTY | L_PTE_WRITE);
- pmdp = pmd_offset(pgd_offset_k(virt), virt);
- if (pmd_none(*pmdp)) {
- ptep = pte_alloc_one_kernel(&init_mm, 0);
- if (ptep == NULL)
- return -ENOMEM;
- /* note: two PMDs will be set */
- pmd_populate_kernel(&init_mm, pmdp, ptep);
- }
+ prot_sect = PMD_TYPE_SECT | PMD_SECT_UNCACHED |
+ PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO);
+
+ if (cpu_architecture() <= CPU_ARCH_ARMv5)
+ prot_sect |= PMD_BIT4;
off = phys - virt;
- for (sz_left = size;
- sz_left >= PAGE_SIZE;
- sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
- ptep = pte_offset_kernel(pmdp, virt);
- set_pte(ptep, __pte((virt + off) | prot_pte));
+
+ while ((virt & 0xfffff || (virt + off) & 0xfffff) && size >= PAGE_SIZE) {
+ exmap_alloc_pte(virt, virt + off, prot_pte);
+
+ virt += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ /* XXX: Not yet.. confuses dspfb -- PFM. */
+#if 0
+ while (size >= (PGDIR_SIZE / 2)) {
+ if (exmap_alloc_sect(virt, virt + off, prot_sect) < 0)
+ break;
+
+ virt += (PGDIR_SIZE / 2);
+ size -= (PGDIR_SIZE / 2);
}
- if (sz_left)
- BUG();
+#endif
+
+ while (size >= PAGE_SIZE) {
+ exmap_alloc_pte(virt, virt + off, prot_pte);
+
+ virt += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ BUG_ON(size);
return 0;
}
+static inline void
+exmap_clear_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end)
+{
+ pte_t *pte;
+
+ pte = pte_offset_map(pmd, addr);
+ do {
+ if (pte_none(*pte))
+ continue;
+
+ pte_clear(&init_mm, addr, pte);
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+
+ pte_unmap(pte - 1);
+}
+
+static inline void
+exmap_clear_pmd_range(pud_t *pud, unsigned long addr, unsigned long end)
+{
+ pmd_t *pmd;
+ unsigned long next;
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+
+ if (addr & (1 << 20))
+ pmd++;
+
+ if ((pmd_val(*pmd) & PMD_TYPE_MASK) == PMD_TYPE_SECT) {
+ *pmd = __pmd(0);
+ clean_pmd_entry(pmd);
+ continue;
+ }
+
+ if (pmd_none_or_clear_bad(pmd))
+ continue;
+
+ exmap_clear_pte_range(pmd, addr, next);
+ } while (pmd++, addr = next, addr != end);
+}
+
+static inline void
+exmap_clear_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end)
+{
+ pud_t *pud;
+ unsigned long next;
+
+ pud = pud_offset(pgd, addr);
+ do {
+ next = pud_addr_end(addr, end);
+ if (pud_none_or_clear_bad(pud))
+ continue;
+
+ exmap_clear_pmd_range(pud, addr, next);
+ } while (pud++, addr = next, addr != end);
+}
+
static void exmap_clear_armmmu(unsigned long virt, unsigned long size)
{
- unsigned long sz_left;
- pmd_t *pmdp;
- pte_t *ptep;
+ unsigned long next, end;
+ pgd_t *pgd;
printk(KERN_DEBUG
"omapdsp: unmapping in ARM MMU, v=0x%08lx, sz=0x%lx\n",
virt, size);
- for (sz_left = size;
- sz_left >= PAGE_SIZE;
- sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
- pmdp = pmd_offset(pgd_offset_k(virt), virt);
- ptep = pte_offset_kernel(pmdp, virt);
- pte_clear(&init_mm, virt, ptep);
- }
- if (sz_left)
- BUG();
+ pgd = pgd_offset_k(virt);
+ end = virt + size;
+ do {
+ next = pgd_addr_end(virt, end);
+ if (pgd_none_or_clear_bad(pgd))
+ continue;
+
+ exmap_clear_pud_range(pgd, virt, next);
+ } while (pgd++, virt = next, virt != end);
}
static int exmap_valid(void *vadr, size_t len)
is_aligned(_dspadr, SZ_1MB)) {
unit = SZ_1MB;
slst = DSPMMU_CAM_L_SLST_1MB;
- order = ORDER_1MB;
} else if ((_size >= SZ_64KB) &&
(is_aligned(_padr, SZ_64KB) || (padr == 0)) &&
is_aligned(_dspadr, SZ_64KB)) {
unit = SZ_64KB;
slst = DSPMMU_CAM_L_SLST_64KB;
- order = ORDER_64KB;
- } else /* if (_size >= SZ_4KB) */ {
+ } else {
unit = SZ_4KB;
slst = DSPMMU_CAM_L_SLST_4KB;
- order = ORDER_4KB;
- }
-#if 0 /* 1KB is not enabled */
- else if (_size >= SZ_1KB) {
- unit = SZ_1KB;
- slst = DSPMMU_CAM_L_SLST_1KB;
- order = XXX;
}
-#endif
+
+ order = get_order(unit);
/* buffer allocation */
if (type == EXMAP_TYPE_MEM) {
struct page *page, *ps, *pe;
- buf = (void *)dsp_mem_get_dma_pages(order);
- if (buf == NULL) {
- status = -ENOMEM;
- goto fail;
+ if ((order == ORDER_1MB) && likely(kmem_pool_1M))
+ buf = mempool_alloc_from_pool(kmem_pool_1M, GFP_KERNEL);
+ else if ((order == ORDER_64KB) && likely(kmem_pool_64K))
+ buf = mempool_alloc_from_pool(kmem_pool_64K,GFP_KERNEL);
+ else {
+ buf = (void *)__get_dma_pages(GFP_KERNEL, order);
+ if (buf == NULL) {
+ status = -ENOMEM;
+ goto fail;
+ }
}
+
/* mark the pages as reserved; this is needed for mmap */
ps = virt_to_page(buf);
pe = virt_to_page(buf + unit);
- for (page = ps; page < pe; page++) {
+
+ for (page = ps; page < pe; page++)
SetPageReserved(page);
- }
+
_padr = __pa(buf);
}
static ssize_t kmem_pool_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int count_1M, count_64K, total;
+ int nr_1M, nr_64K, total;
- count_1M = kmem_pool_1M.count;
- count_64K = kmem_pool_64K.count;
- total = count_1M * SZ_1MB + count_64K * SZ_64KB;
+ nr_1M = kmem_pool_1M->min_nr;
+ nr_64K = kmem_pool_64K->min_nr;
+ total = nr_1M * SZ_1MB + nr_64K * SZ_64KB;
- return sprintf(buf, "0x%x %d %d\n", total, count_1M, count_64K);
+ return sprintf(buf, "0x%x %d %d\n", total, nr_1M, nr_64K);
}
static struct device_attribute dev_attr_kmem_pool = __ATTR_RO(kmem_pool);
return ret;
}
+static void proclist_send_sigbus(struct list_head *list)
+{
+ siginfo_t info;
+ struct proc_list *pl;
+ struct task_struct *tsk;
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = SI_KERNEL;
+ info._sifields._sigfault._addr = NULL;
+
+ /* need to lock tasklist_lock before calling find_task_by_pid_type. */
+ read_lock(&tasklist_lock);
+ list_for_each_entry(pl, list, list_head) {
+ if ((tsk = find_task_by_pid_type(PIDTYPE_PID, pl->pid)) != NULL)
+ send_sig_info(SIGBUS, &info, tsk);
+ }
+ read_unlock(&tasklist_lock);
+}
+
static int dsp_task_flush_buf(struct dsptask *task)
{
unsigned short ttyp = task->ttyp;
case OMAP_DSP_DEVSTATE_ATTACHED:
/* task is working. kill it. */
- {
- siginfo_t info;
- struct proc_list *pl;
-
- dev->state = OMAP_DSP_DEVSTATE_KILLING;
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = SI_KERNEL;
- info._sifields._sigfault._addr = NULL;
- list_for_each_entry(pl, &dev->proc_list, list_head) {
- send_sig_info(SIGBUS, &info, pl->tsk);
- }
- spin_unlock(&dev->state_lock);
- dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_KILL);
- goto invalidate;
- }
+ dev->state = OMAP_DSP_DEVSTATE_KILLING;
+ proclist_send_sigbus(&dev->proc_list);
+ spin_unlock(&dev->state_lock);
+ dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_KILL);
+ goto invalidate;
case OMAP_DSP_DEVSTATE_ADDREQ:
/* open() is waiting. drain it. */
int dsp_tkill(unsigned char minor)
{
struct taskdev *dev;
- siginfo_t info;
- struct proc_list *pl;
if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
printk(KERN_ERR
return -EINVAL;
}
dev->state = OMAP_DSP_DEVSTATE_KILLING;
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = SI_KERNEL;
- info._sifields._sigfault._addr = NULL;
- list_for_each_entry(pl, &dev->proc_list, list_head) {
- send_sig_info(SIGBUS, &info, pl->tsk);
- }
+ proclist_send_sigbus(&dev->proc_list);
spin_unlock(&dev->state_lock);
return dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_KILL);
void mbx1_err_fatal(unsigned char tid)
{
struct dsptask *task = dsptask[tid];
- struct proc_list *pl;
- siginfo_t info;
if ((tid >= TASKDEV_MAX) || (task == NULL)) {
printk(KERN_ERR "mbx: FATAL ERR with illegal tid! %d\n", tid);
return;
}
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = SI_KERNEL;
- info._sifields._sigfault._addr = NULL;
spin_lock(&task->dev->state_lock);
- list_for_each_entry(pl, &task->dev->proc_list, list_head) {
- send_sig_info(SIGBUS, &info, pl->tsk);
- }
+ proclist_send_sigbus(&task->dev->proc_list);
spin_unlock(&task->dev->state_lock);
}
dev = to_taskdev(d);
spin_lock(&dev->state_lock);
list_for_each_entry(pl, &dev->proc_list, list_head) {
- len += sprintf(buf + len, "%d\n", pl->tsk->pid);
+ len += sprintf(buf + len, "%d\n", pl->pid);
}
spin_unlock(&dev->state_lock);