#include <linux/init.h>
#include <linux/module.h>
++ #include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/dmaengine.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
--
--#include <asm/dmaengine.h>
++#include <linux/sh_dma.h>
#include "shdma.h"
#define LOG2_DEFAULT_XFER_SIZE 2
/* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
--static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SHDMA_SLAVE_NUMBER)];
++static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
}
static struct sh_dmae_slave_config *sh_dmae_find_slave(
-- struct sh_dmae_chan *sh_chan, enum sh_dmae_slave_chan_id slave_id)
++ struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param)
{
struct dma_device *dma_dev = sh_chan->common.device;
struct sh_dmae_device *shdev = container_of(dma_dev,
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
-- if ((unsigned)slave_id >= SHDMA_SLAVE_NUMBER)
++ if (param->slave_id >= SH_DMA_SLAVE_NUMBER)
return NULL;
for (i = 0; i < pdata->slave_num; i++)
-- if (pdata->slave[i].slave_id == slave_id)
++ if (pdata->slave[i].slave_id == param->slave_id)
return pdata->slave + i;
return NULL;
if (param) {
struct sh_dmae_slave_config *cfg;
-- cfg = sh_dmae_find_slave(sh_chan, param->slave_id);
++ cfg = sh_dmae_find_slave(sh_chan, param);
if (!cfg)
return -EINVAL;
return ret;
}
--#if defined(CONFIG_CPU_SH4)
++#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
--#if defined(CONFIG_CPU_SH4)
++#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
#else
chanirq_res = errirq_res;
--#endif /* CONFIG_CPU_SH4 */
++#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
chan_probe_err:
sh_dmae_chan_remove(shdev);
eirqres:
--#if defined(CONFIG_CPU_SH4)
++#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
free_irq(errirq, shdev);
eirq_err:
#endif
#include <linux/list.h>
#include <linux/dmaengine.h>
#include <linux/scatterlist.h>
- #include <linux/timer.h>
++ #include <linux/slab.h>
#ifdef CONFIG_SUPERH
#include <asm/sh_bios.h>
struct dma_chan *chan_rx;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
struct device *dma_dev;
-- enum sh_dmae_slave_chan_id slave_tx;
-- enum sh_dmae_slave_chan_id slave_rx;
++ unsigned int slave_tx;
++ unsigned int slave_rx;
struct dma_async_tx_descriptor *desc_tx;
struct dma_async_tx_descriptor *desc_rx[2];
dma_cookie_t cookie_tx;
if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
ret = sci_br_interrupt(irq, ptr);
-- WARN_ONCE(ret == IRQ_NONE,
-- "%s: %d IRQ %d, status %x, control %x\n", __func__,
-- irq, port->line, ssr_status, scr_status);
--
return ret;
}
spin_lock_irqsave(&port->lock, flags);
-- xmit->tail += s->sg_tx.length;
++ xmit->tail += sg_dma_len(&s->sg_tx);
xmit->tail &= UART_XMIT_SIZE - 1;
-- port->icount.tx += s->sg_tx.length;
++ port->icount.tx += sg_dma_len(&s->sg_tx);
async_tx_ack(s->desc_tx);
s->cookie_tx = -EINVAL;
*/
spin_lock_irq(&port->lock);
sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
-- sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) +
++ sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) +
sg->offset;
-- sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE),
++ sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE),
CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE));
-- sg->dma_length = sg->length;
spin_unlock_irq(&port->lock);
-- BUG_ON(!sg->length);
++ BUG_ON(!sg_dma_len(sg));
desc = chan->device->device_prep_slave_sg(chan,
sg, s->sg_len_tx, DMA_TO_DEVICE,
sg_init_table(sg, 1);
sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx,
(int)buf[i] & ~PAGE_MASK);
-- sg->dma_address = dma[i];
-- sg->dma_length = sg->length;
++ sg_dma_address(sg) = dma[i];
}
INIT_WORK(&s->work_rx, work_fn_rx);
* Shared interrupt handling code for IPR and INTC2 types of IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009 Paul Mundt
+ * Copyright (C) 2009, 2010 Paul Mundt
*
* Based on intc2.c and ipr.c
*
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/io.h>
++ #include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/sh_intc.h>
#include <linux/sysdev.h>
#include <linux/list.h>
#include <linux/topology.h>
#include <linux/bitmap.h>
+ #include <linux/cpumask.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
unsigned long handle;
};
+ +struct intc_window {
+ + phys_addr_t phys;
+ + void __iomem *virt;
+ + unsigned long size;
+ +};
+ +
struct intc_desc_int {
struct list_head list;
struct sys_device sysdev;
unsigned int nr_prio;
struct intc_handle_int *sense;
unsigned int nr_sense;
+ + struct intc_window *window;
+ + unsigned int nr_windows;
struct irq_chip chip;
};
unsigned int cpu;
for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
+ #ifdef CONFIG_SMP
+ if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
+ continue;
+ #endif
addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
[_INTC_FN(handle)], irq);
unsigned int cpu;
for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
+ #ifdef CONFIG_SMP
+ if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
+ continue;
+ #endif
addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
[_INTC_FN(handle)], irq);
return 0; /* allow wakeup, but setup hardware in intc_suspend() */
}
+ #ifdef CONFIG_SMP
+ /*
+ * This is held with the irq desc lock held, so we don't require any
+ * additional locking here at the intc desc level. The affinity mask is
+ * later tested in the enable/disable paths.
+ */
+ static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask)
+ {
+ if (!cpumask_intersects(cpumask, cpu_online_mask))
+ return -1;
+
+ cpumask_copy(irq_to_desc(irq)->affinity, cpumask);
+
+ return 0;
+ }
+ #endif
+
static void intc_mask_ack(unsigned int irq)
{
struct intc_desc_int *d = get_intc_desc(irq);
return 0;
}
+ +static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
+ + unsigned long address)
+ +{
+ + struct intc_window *window;
+ + int k;
+ +
+ + /* scan through physical windows and convert address */
+ + for (k = 0; k < d->nr_windows; k++) {
+ + window = d->window + k;
+ +
+ + if (address < window->phys)
+ + continue;
+ +
+ + if (address >= (window->phys + window->size))
+ + continue;
+ +
+ + address -= window->phys;
+ + address += (unsigned long)window->virt;
+ +
+ + return address;
+ + }
+ +
+ + /* no windows defined, register must be 1:1 mapped virt:phys */
+ + return address;
+ +}
+ +
static unsigned int __init intc_get_reg(struct intc_desc_int *d,
- - unsigned long address)
+ + unsigned long address)
{
unsigned int k;
+ + address = intc_phys_to_virt(d, address);
+ +
for (k = 0; k < d->nr_reg; k++) {
if (d->reg[k] == address)
return k;
unsigned int smp)
{
if (value) {
+ + value = intc_phys_to_virt(d, value);
+ +
d->reg[cnt] = value;
#ifdef CONFIG_SMP
d->smp[cnt] = smp;
generic_handle_irq((unsigned int)get_irq_data(irq));
}
- -void __init register_intc_controller(struct intc_desc *desc)
+ +int __init register_intc_controller(struct intc_desc *desc)
{
unsigned int i, k, smp;
struct intc_hw_desc *hw = &desc->hw;
struct intc_desc_int *d;
+ + struct resource *res;
d = kzalloc(sizeof(*d), GFP_NOWAIT);
+ + if (!d)
+ + goto err0;
INIT_LIST_HEAD(&d->list);
list_add(&d->list, &intc_list);
+ + if (desc->num_resources) {
+ + d->nr_windows = desc->num_resources;
+ + d->window = kzalloc(d->nr_windows * sizeof(*d->window),
+ + GFP_NOWAIT);
+ + if (!d->window)
+ + goto err1;
+ +
+ + for (k = 0; k < d->nr_windows; k++) {
+ + res = desc->resource + k;
+ + WARN_ON(resource_type(res) != IORESOURCE_MEM);
+ + d->window[k].phys = res->start;
+ + d->window[k].size = resource_size(res);
+ + d->window[k].virt = ioremap_nocache(res->start,
+ + resource_size(res));
+ + if (!d->window[k].virt)
+ + goto err2;
+ + }
+ + }
+ +
d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
+ + if (!d->reg)
+ + goto err2;
+ +
#ifdef CONFIG_SMP
d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
+ + if (!d->smp)
+ + goto err3;
#endif
k = 0;
if (hw->prio_regs) {
d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
GFP_NOWAIT);
+ + if (!d->prio)
+ + goto err4;
for (i = 0; i < hw->nr_prio_regs; i++) {
smp = IS_SMP(hw->prio_regs[i]);
if (hw->sense_regs) {
d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
GFP_NOWAIT);
+ + if (!d->sense)
+ + goto err5;
for (i = 0; i < hw->nr_sense_regs; i++)
k += save_reg(d, k, hw->sense_regs[i].reg, 0);
d->chip.shutdown = intc_disable;
d->chip.set_type = intc_set_sense;
d->chip.set_wake = intc_set_wake;
+ #ifdef CONFIG_SMP
+ d->chip.set_affinity = intc_set_affinity;
+ #endif
if (hw->ack_regs) {
for (i = 0; i < hw->nr_ack_regs; i++)
/* enable bits matching force_enable after registering irqs */
if (desc->force_enable)
intc_enable_disable_enum(desc, d, desc->force_enable, 1);
+ +
+ + return 0;
+ +err5:
+ + kfree(d->prio);
+ +err4:
+ +#ifdef CONFIG_SMP
+ + kfree(d->smp);
+ +err3:
+ +#endif
+ + kfree(d->reg);
+ +err2:
+ + for (k = 0; k < d->nr_windows; k++)
+ + if (d->window[k].virt)
+ + iounmap(d->window[k].virt);
+ +
+ + kfree(d->window);
+ +err1:
+ + kfree(d);
+ +err0:
+ + pr_err("unable to allocate INTC memory\n");
+ +
+ + return -ENOMEM;
}
static int intc_suspend(struct sys_device *dev, pm_message_t state)