ARM SoC driver specific changes

These changes are all driver specific and cross over between arm-soc
 contents and some other subsystem, in these cases cpufreq, crypto,
 dma, pinctrl, mailbox and usb, and the subsystem owners agreed to
 have these changes merged through arm-soc. As we proceed to untangle
 the dependencies between platform code and driver code, the amount of
 changes in this category is fortunately shrinking, for 3.11 we have
 16 branches here and 101 non-merge changesets, the majority of which
 are for the stedma40 dma engine driver used in the ux500 platform.
 Cleaning up that code touches multiple subsystems, but gets rid
 of the dependency in the end.
 
 The mailbox code moved out from mach-omap2 to drivers/mailbox
 is an intermediate step and is still omap specific at the moment.
 Patches exist to generalize the subsystem and add other drivers
 with the same API, but those did not make it for 3.11.
 
 Conflicts:
 * In cpu-db8500.c results from the removal of the u8500_of_init_devices
   function in combination with the split of u8500_auxdata_lookup.
 
 * In arch/arm/mach-omap2/devices.c, the includes got reshuffled.
   we need to keep linux/wl12xx.h and linux/platform_data/mailbox-omap.h.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIVAwUAUdLnomCrR//JCVInAQJI/A/9FydsQa9sdnzLFgcdX5BeRRwkXLfDifCM
 zDTfUBo+LriKOs7QHblmDg1MnY1UMB2IfrdHD0FsjK7WbZ/91EMAGDPYcI7Fu4+u
 pGStxwWi2v+oCT1jjeOkCPT7hdCqogsSpybYq8itSb+zdvdOi6U35dWEKz8xGqz4
 vTL9gTZbJP0kowkjIcaryk7FIj7BTIvMCW8n55JZEkDe0BuSJGYn5c3Mntut12ZK
 5xM2PeNe2sd3dIdA6XbM2ye/XmYa8xY8Qu4/ijxfH1gnJLvz9Unp96nRXpEbIeMb
 BH2Sro8dxsMCaweIQhSRKGnUWMYO/Rh7/+5EqzJ163Ezthx9nvHXJY2ndWuD7uM1
 IcGrMxLtqP22TEMtZAVEATDp/5ymxEo5GM+eayUojQDn213wOJjRs5xz5IBsH4KM
 8CM/gpadWmLjWku72yEV4lryLcdS0NVQzpTnEbILEGOU4u7qJtxRAp7x7tWBtFg8
 4m/eWcSVk/U2SYbXmQHsfukuWgKY0cnZbctPcdnaqXwTP7toJEAK3gxoMtWh49Jq
 2M2PVFyFejaaq5b/71wAJ7ePYw56H0N/F3RsGpPE55AY15++gSoQ+3t2Si68hDw8
 NtyJMkQYpTvtqJbHXWpQQ3Zfs7pDBe01WDV7i+m4JTNggxUDaO/t1Fqp+fEksm4J
 r+luEf5Gcgk=
 =mJsI
 -----END PGP SIGNATURE-----

Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC driver specific changes from Arnd Bergmann:
 "These changes are all driver specific and cross over between arm-soc
  contents and some other subsystem, in these cases cpufreq, crypto,
  dma, pinctrl, mailbox and usb, and the subsystem owners agreed to have
  these changes merged through arm-soc.

  As we proceed to untangle the dependencies between platform code and
  driver code, the amount of changes in this category is fortunately
  shrinking, for 3.11 we have 16 branches here and 101 non-merge
  changesets, the majority of which are for the stedma40 dma engine
  driver used in the ux500 platform.  Cleaning up that code touches
  multiple subsystems, but gets rid of the dependency in the end.

  The mailbox code moved out from mach-omap2 to drivers/mailbox is an
  intermediate step and is still omap specific at the moment.  Patches
  exist to generalize the subsystem and add other drivers with the same
  API, but those did not make it for 3.11."

* tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (101 commits)
  crypto: ux500: use dmaengine_submit API
  crypto: ux500: use dmaengine_prep_slave_sg API
  crypto: ux500: use dmaengine_device_control API
  crypto: ux500/crypt: add missing __iomem qualifiers
  crypto: ux500/hash: add missing static qualifiers
  crypto: ux500/hash: use readl on iomem addresses
  dmaengine: ste_dma40: Declare memcpy config as static
  ARM: ux500: Remove mop500_snowball_ethernet_clock_enable()
  ARM: ux500: Correct the EN_3v3 regulator's on/off GPIO
  ARM: ux500: Provide a AB8500 GPIO Device Tree node
  gpio: rcar: fix gpio_rcar_of_table
  gpio-rcar: Remove #ifdef CONFIG_OF around OF-specific sections
  gpio-rcar: Reference core gpio documentation in the DT bindings
  clk: exynos5250: Add enum entries for divider clock of i2s1 and i2s2
  ARM: dts: Update Samsung I2S documentation
  ARM: dts: add clock provider information for i2s controllers in Exynos5250
  ARM: dts: add Exynos audio subsystem clock controller node
  clk: samsung: register audio subsystem clocks using common clock framework
  ARM: dts: use #include for all device trees for Samsung
  pinctrl: s3c24xx: use correct header for chained_irq functions
  ...
This commit is contained in:
Linus Torvalds 2013-07-02 14:33:21 -07:00
commit 0bf6a210a4
112 changed files with 3168 additions and 1651 deletions

View file

@ -17,6 +17,8 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/amba/bus.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/dma-ste-dma40.h>
@ -45,15 +47,63 @@
#define D40_LCLA_LINK_PER_EVENT_GRP 128
#define D40_LCLA_END D40_LCLA_LINK_PER_EVENT_GRP
/* Max number of logical channels per physical channel */
#define D40_MAX_LOG_CHAN_PER_PHY 32
/* Attempts before giving up to trying to get pages that are aligned */
#define MAX_LCLA_ALLOC_ATTEMPTS 256
/* Bit markings for allocation map */
#define D40_ALLOC_FREE (1 << 31)
#define D40_ALLOC_PHY (1 << 30)
#define D40_ALLOC_FREE BIT(31)
#define D40_ALLOC_PHY BIT(30)
#define D40_ALLOC_LOG_FREE 0
#define MAX(a, b) (((a) < (b)) ? (b) : (a))
#define D40_MEMCPY_MAX_CHANS 8
/* Reserved event lines for memcpy only. */
#define DB8500_DMA_MEMCPY_EV_0 51
#define DB8500_DMA_MEMCPY_EV_1 56
#define DB8500_DMA_MEMCPY_EV_2 57
#define DB8500_DMA_MEMCPY_EV_3 58
#define DB8500_DMA_MEMCPY_EV_4 59
#define DB8500_DMA_MEMCPY_EV_5 60
static int dma40_memcpy_channels[] = {
DB8500_DMA_MEMCPY_EV_0,
DB8500_DMA_MEMCPY_EV_1,
DB8500_DMA_MEMCPY_EV_2,
DB8500_DMA_MEMCPY_EV_3,
DB8500_DMA_MEMCPY_EV_4,
DB8500_DMA_MEMCPY_EV_5,
};
/* Default configuration for physcial memcpy */
static struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
.mode = STEDMA40_MODE_PHYSICAL,
.dir = DMA_MEM_TO_MEM,
.src_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_info.psize = STEDMA40_PSIZE_PHY_1,
.src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
.dst_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_info.psize = STEDMA40_PSIZE_PHY_1,
.dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
};
/* Default configuration for logical memcpy */
static struct stedma40_chan_cfg dma40_memcpy_conf_log = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = DMA_MEM_TO_MEM,
.src_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_info.psize = STEDMA40_PSIZE_LOG_1,
.src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
.dst_info.data_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_info.psize = STEDMA40_PSIZE_LOG_1,
.dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
};
/**
* enum 40_command - The different commands and/or statuses.
@ -171,6 +221,9 @@ static u32 d40_backup_regs_chan[] = {
D40_CHAN_REG_SDLNK,
};
#define BACKUP_REGS_SZ_MAX ((BACKUP_REGS_SZ_V4A > BACKUP_REGS_SZ_V4B) ? \
BACKUP_REGS_SZ_V4A : BACKUP_REGS_SZ_V4B)
/**
* struct d40_interrupt_lookup - lookup table for interrupt handler
*
@ -471,6 +524,8 @@ struct d40_gen_dmac {
* @phy_start: Physical memory start of the DMA registers.
* @phy_size: Size of the DMA register map.
* @irq: The IRQ number.
* @num_memcpy_chans: The number of channels used for memcpy (mem-to-mem
* transfers).
* @num_phy_chans: The number of physical channels. Read from HW. This
* is the number of available channels for this driver, not counting "Secure
* mode" allocated physical channels.
@ -514,6 +569,7 @@ struct d40_base {
phys_addr_t phy_start;
resource_size_t phy_size;
int irq;
int num_memcpy_chans;
int num_phy_chans;
int num_log_chans;
struct device_dma_parameters dma_parms;
@ -534,7 +590,7 @@ struct d40_base {
resource_size_t lcpa_size;
struct kmem_cache *desc_slab;
u32 reg_val_backup[BACKUP_REGS_SZ];
u32 reg_val_backup_v4[MAX(BACKUP_REGS_SZ_V4A, BACKUP_REGS_SZ_V4B)];
u32 reg_val_backup_v4[BACKUP_REGS_SZ_MAX];
u32 *reg_val_backup_chan;
u16 gcc_pwr_off_mask;
bool initialized;
@ -792,7 +848,7 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc)
* that uses linked lists.
*/
if (!(chan->phy_chan->use_soft_lli &&
chan->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM))
chan->dma_cfg.dir == DMA_DEV_TO_MEM))
curr_lcla = d40_lcla_alloc_one(chan, desc);
first_lcla = curr_lcla;
@ -954,20 +1010,21 @@ static int d40_psize_2_burst_size(bool is_log, int psize)
/*
* The dma only supports transmitting packages up to
* STEDMA40_MAX_SEG_SIZE << data_width. Calculate the total number of
* dma elements required to send the entire sg list
* STEDMA40_MAX_SEG_SIZE * data_width, where data_width is stored in Bytes.
*
* Calculate the total number of dma elements required to send the entire sg list.
*/
static int d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2)
{
int dmalen;
u32 max_w = max(data_width1, data_width2);
u32 min_w = min(data_width1, data_width2);
u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);
u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w);
if (seg_max > STEDMA40_MAX_SEG_SIZE)
seg_max -= (1 << max_w);
seg_max -= max_w;
if (!IS_ALIGNED(size, 1 << max_w))
if (!IS_ALIGNED(size, max_w))
return -EINVAL;
if (size <= seg_max)
@ -1257,21 +1314,17 @@ static void __d40_config_set_event(struct d40_chan *d40c,
static void d40_config_set_event(struct d40_chan *d40c,
enum d40_events event_type)
{
/* Enable event line connected to device (or memcpy) */
if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) ||
(d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) {
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type);
/* Enable event line connected to device (or memcpy) */
if ((d40c->dma_cfg.dir == DMA_DEV_TO_MEM) ||
(d40c->dma_cfg.dir == DMA_DEV_TO_DEV))
__d40_config_set_event(d40c, event_type, event,
D40_CHAN_REG_SSLNK);
}
if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) {
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
if (d40c->dma_cfg.dir != DMA_DEV_TO_MEM)
__d40_config_set_event(d40c, event_type, event,
D40_CHAN_REG_SDLNK);
}
}
static u32 d40_chan_has_events(struct d40_chan *d40c)
@ -1417,7 +1470,7 @@ static u32 d40_residue(struct d40_chan *d40c)
>> D40_SREG_ELEM_PHY_ECNT_POS;
}
return num_elt * (1 << d40c->dma_cfg.dst_info.data_width);
return num_elt * d40c->dma_cfg.dst_info.data_width;
}
static bool d40_tx_is_linked(struct d40_chan *d40c)
@ -1693,7 +1746,7 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
}
/* ACK interrupt */
writel(1 << idx, base->virtbase + il[row].clr);
writel(BIT(idx), base->virtbase + il[row].clr);
spin_lock(&d40c->lock);
@ -1715,8 +1768,6 @@ static int d40_validate_conf(struct d40_chan *d40c,
struct stedma40_chan_cfg *conf)
{
int res = 0;
u32 dst_event_group = D40_TYPE_TO_GROUP(conf->dst_dev_type);
u32 src_event_group = D40_TYPE_TO_GROUP(conf->src_dev_type);
bool is_log = conf->mode == STEDMA40_MODE_LOGICAL;
if (!conf->dir) {
@ -1724,48 +1775,14 @@ static int d40_validate_conf(struct d40_chan *d40c,
res = -EINVAL;
}
if (conf->dst_dev_type != STEDMA40_DEV_DST_MEMORY &&
d40c->base->plat_data->dev_tx[conf->dst_dev_type] == 0 &&
d40c->runtime_addr == 0) {
chan_err(d40c, "Invalid TX channel address (%d)\n",
conf->dst_dev_type);
if ((is_log && conf->dev_type > d40c->base->num_log_chans) ||
(!is_log && conf->dev_type > d40c->base->num_phy_chans) ||
(conf->dev_type < 0)) {
chan_err(d40c, "Invalid device type (%d)\n", conf->dev_type);
res = -EINVAL;
}
if (conf->src_dev_type != STEDMA40_DEV_SRC_MEMORY &&
d40c->base->plat_data->dev_rx[conf->src_dev_type] == 0 &&
d40c->runtime_addr == 0) {
chan_err(d40c, "Invalid RX channel address (%d)\n",
conf->src_dev_type);
res = -EINVAL;
}
if (conf->dir == STEDMA40_MEM_TO_PERIPH &&
dst_event_group == STEDMA40_DEV_DST_MEMORY) {
chan_err(d40c, "Invalid dst\n");
res = -EINVAL;
}
if (conf->dir == STEDMA40_PERIPH_TO_MEM &&
src_event_group == STEDMA40_DEV_SRC_MEMORY) {
chan_err(d40c, "Invalid src\n");
res = -EINVAL;
}
if (src_event_group == STEDMA40_DEV_SRC_MEMORY &&
dst_event_group == STEDMA40_DEV_DST_MEMORY && is_log) {
chan_err(d40c, "No event line\n");
res = -EINVAL;
}
if (conf->dir == STEDMA40_PERIPH_TO_PERIPH &&
(src_event_group != dst_event_group)) {
chan_err(d40c, "Invalid event group\n");
res = -EINVAL;
}
if (conf->dir == STEDMA40_PERIPH_TO_PERIPH) {
if (conf->dir == DMA_DEV_TO_DEV) {
/*
* DMAC HW supports it. Will be added to this driver,
* in case any dma client requires it.
@ -1775,9 +1792,9 @@ static int d40_validate_conf(struct d40_chan *d40c,
}
if (d40_psize_2_burst_size(is_log, conf->src_info.psize) *
(1 << conf->src_info.data_width) !=
conf->src_info.data_width !=
d40_psize_2_burst_size(is_log, conf->dst_info.psize) *
(1 << conf->dst_info.data_width)) {
conf->dst_info.data_width) {
/*
* The DMAC hardware only supports
* src (burst x width) == dst (burst x width)
@ -1819,8 +1836,8 @@ static bool d40_alloc_mask_set(struct d40_phy_res *phy,
if (phy->allocated_src == D40_ALLOC_FREE)
phy->allocated_src = D40_ALLOC_LOG_FREE;
if (!(phy->allocated_src & (1 << log_event_line))) {
phy->allocated_src |= 1 << log_event_line;
if (!(phy->allocated_src & BIT(log_event_line))) {
phy->allocated_src |= BIT(log_event_line);
goto found;
} else
goto not_found;
@ -1831,8 +1848,8 @@ static bool d40_alloc_mask_set(struct d40_phy_res *phy,
if (phy->allocated_dst == D40_ALLOC_FREE)
phy->allocated_dst = D40_ALLOC_LOG_FREE;
if (!(phy->allocated_dst & (1 << log_event_line))) {
phy->allocated_dst |= 1 << log_event_line;
if (!(phy->allocated_dst & BIT(log_event_line))) {
phy->allocated_dst |= BIT(log_event_line);
goto found;
} else
goto not_found;
@ -1862,11 +1879,11 @@ static bool d40_alloc_mask_free(struct d40_phy_res *phy, bool is_src,
/* Logical channel */
if (is_src) {
phy->allocated_src &= ~(1 << log_event_line);
phy->allocated_src &= ~BIT(log_event_line);
if (phy->allocated_src == D40_ALLOC_LOG_FREE)
phy->allocated_src = D40_ALLOC_FREE;
} else {
phy->allocated_dst &= ~(1 << log_event_line);
phy->allocated_dst &= ~BIT(log_event_line);
if (phy->allocated_dst == D40_ALLOC_LOG_FREE)
phy->allocated_dst = D40_ALLOC_FREE;
}
@ -1882,7 +1899,7 @@ out:
static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
{
int dev_type;
int dev_type = d40c->dma_cfg.dev_type;
int event_group;
int event_line;
struct d40_phy_res *phys;
@ -1896,14 +1913,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
phys = d40c->base->phy_res;
num_phy_chans = d40c->base->num_phy_chans;
if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
dev_type = d40c->dma_cfg.src_dev_type;
if (d40c->dma_cfg.dir == DMA_DEV_TO_MEM) {
log_num = 2 * dev_type;
is_src = true;
} else if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH ||
d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
} else if (d40c->dma_cfg.dir == DMA_MEM_TO_DEV ||
d40c->dma_cfg.dir == DMA_MEM_TO_MEM) {
/* dst event lines are used for logical memcpy */
dev_type = d40c->dma_cfg.dst_dev_type;
log_num = 2 * dev_type + 1;
is_src = false;
} else
@ -1913,7 +1928,7 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
event_line = D40_TYPE_TO_EVENT(dev_type);
if (!is_log) {
if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
if (d40c->dma_cfg.dir == DMA_MEM_TO_MEM) {
/* Find physical half channel */
if (d40c->dma_cfg.use_fixed_channel) {
i = d40c->dma_cfg.phy_channel;
@ -2014,14 +2029,23 @@ static int d40_config_memcpy(struct d40_chan *d40c)
dma_cap_mask_t cap = d40c->chan.device->cap_mask;
if (dma_has_cap(DMA_MEMCPY, cap) && !dma_has_cap(DMA_SLAVE, cap)) {
d40c->dma_cfg = *d40c->base->plat_data->memcpy_conf_log;
d40c->dma_cfg.src_dev_type = STEDMA40_DEV_SRC_MEMORY;
d40c->dma_cfg.dst_dev_type = d40c->base->plat_data->
memcpy[d40c->chan.chan_id];
d40c->dma_cfg = dma40_memcpy_conf_log;
d40c->dma_cfg.dev_type = dma40_memcpy_channels[d40c->chan.chan_id];
d40_log_cfg(&d40c->dma_cfg,
&d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
} else if (dma_has_cap(DMA_MEMCPY, cap) &&
dma_has_cap(DMA_SLAVE, cap)) {
d40c->dma_cfg = *d40c->base->plat_data->memcpy_conf_phy;
d40c->dma_cfg = dma40_memcpy_conf_phy;
/* Generate interrrupt at end of transfer or relink. */
d40c->dst_def_cfg |= BIT(D40_SREG_CFG_TIM_POS);
/* Generate interrupt on error. */
d40c->src_def_cfg |= BIT(D40_SREG_CFG_EIM_POS);
d40c->dst_def_cfg |= BIT(D40_SREG_CFG_EIM_POS);
} else {
chan_err(d40c, "No memcpy\n");
return -EINVAL;
@ -2034,7 +2058,7 @@ static int d40_free_dma(struct d40_chan *d40c)
{
int res = 0;
u32 event;
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type);
struct d40_phy_res *phy = d40c->phy_chan;
bool is_src;
@ -2052,14 +2076,12 @@ static int d40_free_dma(struct d40_chan *d40c)
return -EINVAL;
}
if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH ||
d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
if (d40c->dma_cfg.dir == DMA_MEM_TO_DEV ||
d40c->dma_cfg.dir == DMA_MEM_TO_MEM)
is_src = false;
} else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
else if (d40c->dma_cfg.dir == DMA_DEV_TO_MEM)
is_src = true;
} else {
else {
chan_err(d40c, "Unknown direction\n");
return -EINVAL;
}
@ -2100,7 +2122,7 @@ static bool d40_is_paused(struct d40_chan *d40c)
unsigned long flags;
void __iomem *active_reg;
u32 status;
u32 event;
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dev_type);
spin_lock_irqsave(&d40c->lock, flags);
@ -2119,12 +2141,10 @@ static bool d40_is_paused(struct d40_chan *d40c)
goto _exit;
}
if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH ||
d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
if (d40c->dma_cfg.dir == DMA_MEM_TO_DEV ||
d40c->dma_cfg.dir == DMA_MEM_TO_MEM) {
status = readl(chanbase + D40_CHAN_REG_SDLNK);
} else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
} else if (d40c->dma_cfg.dir == DMA_DEV_TO_MEM) {
status = readl(chanbase + D40_CHAN_REG_SSLNK);
} else {
chan_err(d40c, "Unknown direction\n");
@ -2255,24 +2275,6 @@ err:
return NULL;
}
static dma_addr_t
d40_get_dev_addr(struct d40_chan *chan, enum dma_transfer_direction direction)
{
struct stedma40_platform_data *plat = chan->base->plat_data;
struct stedma40_chan_cfg *cfg = &chan->dma_cfg;
dma_addr_t addr = 0;
if (chan->runtime_addr)
return chan->runtime_addr;
if (direction == DMA_DEV_TO_MEM)
addr = plat->dev_rx[cfg->src_dev_type];
else if (direction == DMA_MEM_TO_DEV)
addr = plat->dev_tx[cfg->dst_dev_type];
return addr;
}
static struct dma_async_tx_descriptor *
d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
struct scatterlist *sg_dst, unsigned int sg_len,
@ -2299,14 +2301,10 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
if (sg_next(&sg_src[sg_len - 1]) == sg_src)
desc->cyclic = true;
if (direction != DMA_TRANS_NONE) {
dma_addr_t dev_addr = d40_get_dev_addr(chan, direction);
if (direction == DMA_DEV_TO_MEM)
src_dev_addr = dev_addr;
else if (direction == DMA_MEM_TO_DEV)
dst_dev_addr = dev_addr;
}
if (direction == DMA_DEV_TO_MEM)
src_dev_addr = chan->runtime_addr;
else if (direction == DMA_MEM_TO_DEV)
dst_dev_addr = chan->runtime_addr;
if (chan_is_logical(chan))
ret = d40_prep_sg_log(chan, desc, sg_src, sg_dst,
@ -2366,7 +2364,7 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src)
u32 rtreg;
u32 event = D40_TYPE_TO_EVENT(dev_type);
u32 group = D40_TYPE_TO_GROUP(dev_type);
u32 bit = 1 << event;
u32 bit = BIT(event);
u32 prioreg;
struct d40_gen_dmac *dmac = &d40c->base->gen_dmac;
@ -2397,13 +2395,57 @@ static void d40_set_prio_realtime(struct d40_chan *d40c)
if (d40c->base->rev < 3)
return;
if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) ||
(d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH))
__d40_set_prio_rt(d40c, d40c->dma_cfg.src_dev_type, true);
if ((d40c->dma_cfg.dir == DMA_DEV_TO_MEM) ||
(d40c->dma_cfg.dir == DMA_DEV_TO_DEV))
__d40_set_prio_rt(d40c, d40c->dma_cfg.dev_type, true);
if ((d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH) ||
(d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH))
__d40_set_prio_rt(d40c, d40c->dma_cfg.dst_dev_type, false);
if ((d40c->dma_cfg.dir == DMA_MEM_TO_DEV) ||
(d40c->dma_cfg.dir == DMA_DEV_TO_DEV))
__d40_set_prio_rt(d40c, d40c->dma_cfg.dev_type, false);
}
#define D40_DT_FLAGS_MODE(flags) ((flags >> 0) & 0x1)
#define D40_DT_FLAGS_DIR(flags) ((flags >> 1) & 0x1)
#define D40_DT_FLAGS_BIG_ENDIAN(flags) ((flags >> 2) & 0x1)
#define D40_DT_FLAGS_FIXED_CHAN(flags) ((flags >> 3) & 0x1)
static struct dma_chan *d40_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
struct stedma40_chan_cfg cfg;
dma_cap_mask_t cap;
u32 flags;
memset(&cfg, 0, sizeof(struct stedma40_chan_cfg));
dma_cap_zero(cap);
dma_cap_set(DMA_SLAVE, cap);
cfg.dev_type = dma_spec->args[0];
flags = dma_spec->args[2];
switch (D40_DT_FLAGS_MODE(flags)) {
case 0: cfg.mode = STEDMA40_MODE_LOGICAL; break;
case 1: cfg.mode = STEDMA40_MODE_PHYSICAL; break;
}
switch (D40_DT_FLAGS_DIR(flags)) {
case 0:
cfg.dir = DMA_MEM_TO_DEV;
cfg.dst_info.big_endian = D40_DT_FLAGS_BIG_ENDIAN(flags);
break;
case 1:
cfg.dir = DMA_DEV_TO_MEM;
cfg.src_info.big_endian = D40_DT_FLAGS_BIG_ENDIAN(flags);
break;
}
if (D40_DT_FLAGS_FIXED_CHAN(flags)) {
cfg.phy_channel = dma_spec->args[1];
cfg.use_fixed_channel = true;
}
return dma_request_channel(cap, stedma40_filter, &cfg);
}
/* DMA ENGINE functions */
@ -2435,23 +2477,21 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
}
pm_runtime_get_sync(d40c->base->dev);
/* Fill in basic CFG register values */
d40_phy_cfg(&d40c->dma_cfg, &d40c->src_def_cfg,
&d40c->dst_def_cfg, chan_is_logical(d40c));
d40_set_prio_realtime(d40c);
if (chan_is_logical(d40c)) {
d40_log_cfg(&d40c->dma_cfg,
&d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)
if (d40c->dma_cfg.dir == DMA_DEV_TO_MEM)
d40c->lcpa = d40c->base->lcpa_base +
d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE;
d40c->dma_cfg.dev_type * D40_LCPA_CHAN_SIZE;
else
d40c->lcpa = d40c->base->lcpa_base +
d40c->dma_cfg.dst_dev_type *
d40c->dma_cfg.dev_type *
D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA;
/* Unmask the Global Interrupt Mask. */
d40c->src_def_cfg |= BIT(D40_SREG_CFG_LOG_GIM_POS);
d40c->dst_def_cfg |= BIT(D40_SREG_CFG_LOG_GIM_POS);
}
dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n",
@ -2641,33 +2681,10 @@ static void d40_terminate_all(struct dma_chan *chan)
static int
dma40_config_to_halfchannel(struct d40_chan *d40c,
struct stedma40_half_channel_info *info,
enum dma_slave_buswidth width,
u32 maxburst)
{
enum stedma40_periph_data_width addr_width;
int psize;
switch (width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
addr_width = STEDMA40_BYTE_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
addr_width = STEDMA40_HALFWORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
addr_width = STEDMA40_WORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_8_BYTES:
addr_width = STEDMA40_DOUBLEWORD_WIDTH;
break;
default:
dev_err(d40c->base->dev,
"illegal peripheral address width "
"requested (%d)\n",
width);
return -EINVAL;
}
if (chan_is_logical(d40c)) {
if (maxburst >= 16)
psize = STEDMA40_PSIZE_LOG_16;
@ -2688,7 +2705,6 @@ dma40_config_to_halfchannel(struct d40_chan *d40c,
psize = STEDMA40_PSIZE_PHY_1;
}
info->data_width = addr_width;
info->psize = psize;
info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
@ -2712,21 +2728,14 @@ static int d40_set_runtime_config(struct dma_chan *chan,
dst_maxburst = config->dst_maxburst;
if (config->direction == DMA_DEV_TO_MEM) {
dma_addr_t dev_addr_rx =
d40c->base->plat_data->dev_rx[cfg->src_dev_type];
config_addr = config->src_addr;
if (dev_addr_rx)
dev_dbg(d40c->base->dev,
"channel has a pre-wired RX address %08x "
"overriding with %08x\n",
dev_addr_rx, config_addr);
if (cfg->dir != STEDMA40_PERIPH_TO_MEM)
if (cfg->dir != DMA_DEV_TO_MEM)
dev_dbg(d40c->base->dev,
"channel was not configured for peripheral "
"to memory transfer (%d) overriding\n",
cfg->dir);
cfg->dir = STEDMA40_PERIPH_TO_MEM;
cfg->dir = DMA_DEV_TO_MEM;
/* Configure the memory side */
if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
@ -2735,21 +2744,14 @@ static int d40_set_runtime_config(struct dma_chan *chan,
dst_maxburst = src_maxburst;
} else if (config->direction == DMA_MEM_TO_DEV) {
dma_addr_t dev_addr_tx =
d40c->base->plat_data->dev_tx[cfg->dst_dev_type];
config_addr = config->dst_addr;
if (dev_addr_tx)
dev_dbg(d40c->base->dev,
"channel has a pre-wired TX address %08x "
"overriding with %08x\n",
dev_addr_tx, config_addr);
if (cfg->dir != STEDMA40_MEM_TO_PERIPH)
if (cfg->dir != DMA_MEM_TO_DEV)
dev_dbg(d40c->base->dev,
"channel was not configured for memory "
"to peripheral transfer (%d) overriding\n",
cfg->dir);
cfg->dir = STEDMA40_MEM_TO_PERIPH;
cfg->dir = DMA_MEM_TO_DEV;
/* Configure the memory side */
if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
@ -2763,6 +2765,11 @@ static int d40_set_runtime_config(struct dma_chan *chan,
return -EINVAL;
}
if (config_addr <= 0) {
dev_err(d40c->base->dev, "no address supplied\n");
return -EINVAL;
}
if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
dev_err(d40c->base->dev,
"src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
@ -2781,14 +2788,24 @@ static int d40_set_runtime_config(struct dma_chan *chan,
src_maxburst = dst_maxburst * dst_addr_width / src_addr_width;
}
/* Only valid widths are; 1, 2, 4 and 8. */
if (src_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED ||
src_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES ||
dst_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED ||
dst_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES ||
((src_addr_width > 1) && (src_addr_width & 1)) ||
((dst_addr_width > 1) && (dst_addr_width & 1)))
return -EINVAL;
cfg->src_info.data_width = src_addr_width;
cfg->dst_info.data_width = dst_addr_width;
ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
src_addr_width,
src_maxburst);
if (ret)
return ret;
ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
dst_addr_width,
dst_maxburst);
if (ret)
return ret;
@ -2797,8 +2814,7 @@ static int d40_set_runtime_config(struct dma_chan *chan,
if (chan_is_logical(d40c))
d40_log_cfg(cfg, &d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
else
d40_phy_cfg(cfg, &d40c->src_def_cfg,
&d40c->dst_def_cfg, false);
d40_phy_cfg(cfg, &d40c->src_def_cfg, &d40c->dst_def_cfg);
/* These settings will take precedence later */
d40c->runtime_addr = config_addr;
@ -2929,7 +2945,7 @@ static int __init d40_dmaengine_init(struct d40_base *base,
}
d40_chan_init(base, &base->dma_memcpy, base->log_chans,
base->num_log_chans, base->plat_data->memcpy_len);
base->num_log_chans, base->num_memcpy_chans);
dma_cap_zero(base->dma_memcpy.cap_mask);
dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask);
@ -3123,13 +3139,14 @@ static int __init d40_phy_res_init(struct d40_base *base)
static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
{
struct stedma40_platform_data *plat_data;
struct stedma40_platform_data *plat_data = pdev->dev.platform_data;
struct clk *clk = NULL;
void __iomem *virtbase = NULL;
struct resource *res = NULL;
struct d40_base *base = NULL;
int num_log_chans = 0;
int num_phy_chans;
int num_memcpy_chans;
int clk_ret = -EINVAL;
int i;
u32 pid;
@ -3189,8 +3206,10 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
* DB8540v1 has revision 4
*/
rev = AMBA_REV_BITS(pid);
plat_data = pdev->dev.platform_data;
if (rev < 2) {
d40_err(&pdev->dev, "hardware revision: %d is not supported", rev);
goto failure;
}
/* The number of physical channels on this HW */
if (plat_data->num_of_phy_chans)
@ -3198,26 +3217,20 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
else
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n",
rev, res->start, num_phy_chans);
/* The number of channels used for memcpy */
if (plat_data->num_of_memcpy_chans)
num_memcpy_chans = plat_data->num_of_memcpy_chans;
else
num_memcpy_chans = ARRAY_SIZE(dma40_memcpy_channels);
if (rev < 2) {
d40_err(&pdev->dev, "hardware revision: %d is not supported",
rev);
goto failure;
}
num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY;
/* Count the number of logical channels in use */
for (i = 0; i < plat_data->dev_len; i++)
if (plat_data->dev_rx[i] != 0)
num_log_chans++;
for (i = 0; i < plat_data->dev_len; i++)
if (plat_data->dev_tx[i] != 0)
num_log_chans++;
dev_info(&pdev->dev,
"hardware rev: %d @ 0x%x with %d physical and %d logical channels\n",
rev, res->start, num_phy_chans, num_log_chans);
base = kzalloc(ALIGN(sizeof(struct d40_base), 4) +
(num_phy_chans + num_log_chans + plat_data->memcpy_len) *
(num_phy_chans + num_log_chans + num_memcpy_chans) *
sizeof(struct d40_chan), GFP_KERNEL);
if (base == NULL) {
@ -3227,6 +3240,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
base->rev = rev;
base->clk = clk;
base->num_memcpy_chans = num_memcpy_chans;
base->num_phy_chans = num_phy_chans;
base->num_log_chans = num_log_chans;
base->phy_start = res->start;
@ -3278,17 +3292,11 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
if (!base->lookup_phy_chans)
goto failure;
if (num_log_chans + plat_data->memcpy_len) {
/*
* The max number of logical channels are event lines for all
* src devices and dst devices
*/
base->lookup_log_chans = kzalloc(plat_data->dev_len * 2 *
sizeof(struct d40_chan *),
GFP_KERNEL);
if (!base->lookup_log_chans)
goto failure;
}
base->lookup_log_chans = kzalloc(num_log_chans *
sizeof(struct d40_chan *),
GFP_KERNEL);
if (!base->lookup_log_chans)
goto failure;
base->reg_val_backup_chan = kmalloc(base->num_phy_chans *
sizeof(d40_backup_regs_chan),
@ -3472,17 +3480,82 @@ failure:
return ret;
}
static int __init d40_of_probe(struct platform_device *pdev,
struct device_node *np)
{
struct stedma40_platform_data *pdata;
int num_phy = 0, num_memcpy = 0, num_disabled = 0;
const const __be32 *list;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct stedma40_platform_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
/* If absent this value will be obtained from h/w. */
of_property_read_u32(np, "dma-channels", &num_phy);
if (num_phy > 0)
pdata->num_of_phy_chans = num_phy;
list = of_get_property(np, "memcpy-channels", &num_memcpy);
num_memcpy /= sizeof(*list);
if (num_memcpy > D40_MEMCPY_MAX_CHANS || num_memcpy <= 0) {
d40_err(&pdev->dev,
"Invalid number of memcpy channels specified (%d)\n",
num_memcpy);
return -EINVAL;
}
pdata->num_of_memcpy_chans = num_memcpy;
of_property_read_u32_array(np, "memcpy-channels",
dma40_memcpy_channels,
num_memcpy);
list = of_get_property(np, "disabled-channels", &num_disabled);
num_disabled /= sizeof(*list);
if (num_disabled > STEDMA40_MAX_PHYS || num_disabled < 0) {
d40_err(&pdev->dev,
"Invalid number of disabled channels specified (%d)\n",
num_disabled);
return -EINVAL;
}
of_property_read_u32_array(np, "disabled-channels",
pdata->disabled_channels,
num_disabled);
pdata->disabled_channels[num_disabled] = -1;
pdev->dev.platform_data = pdata;
return 0;
}
static int __init d40_probe(struct platform_device *pdev)
{
int err;
struct stedma40_platform_data *plat_data = pdev->dev.platform_data;
struct device_node *np = pdev->dev.of_node;
int ret = -ENOENT;
struct d40_base *base;
struct d40_base *base = NULL;
struct resource *res = NULL;
int num_reserved_chans;
u32 val;
base = d40_hw_detect_init(pdev);
if (!plat_data) {
if (np) {
if(d40_of_probe(pdev, np)) {
ret = -ENOMEM;
goto failure;
}
} else {
d40_err(&pdev->dev, "No pdata or Device Tree provided\n");
goto failure;
}
}
base = d40_hw_detect_init(pdev);
if (!base)
goto failure;
@ -3575,6 +3648,7 @@ static int __init d40_probe(struct platform_device *pdev)
base->lcpa_regulator = regulator_get(base->dev, "lcla_esram");
if (IS_ERR(base->lcpa_regulator)) {
d40_err(&pdev->dev, "Failed to get lcpa_regulator\n");
ret = PTR_ERR(base->lcpa_regulator);
base->lcpa_regulator = NULL;
goto failure;
}
@ -3590,19 +3664,26 @@ static int __init d40_probe(struct platform_device *pdev)
}
base->initialized = true;
err = d40_dmaengine_init(base, num_reserved_chans);
if (err)
ret = d40_dmaengine_init(base, num_reserved_chans);
if (ret)
goto failure;
base->dev->dma_parms = &base->dma_parms;
err = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE);
if (err) {
ret = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE);
if (ret) {
d40_err(&pdev->dev, "Failed to set dma max seg size\n");
goto failure;
}
d40_hw_init(base);
if (np) {
ret = of_dma_controller_register(np, d40_xlate, NULL);
if (ret)
dev_err(&pdev->dev,
"could not register of_dma_controller\n");
}
dev_info(base->dev, "initialized\n");
return 0;
@ -3656,11 +3737,17 @@ failure:
return ret;
}
static const struct of_device_id d40_match[] = {
{ .compatible = "stericsson,dma40", },
{}
};
static struct platform_driver d40_driver = {
.driver = {
.owner = THIS_MODULE,
.name = D40_NAME,
.pm = DMA40_PM_OPS,
.of_match_table = d40_match,
},
};

View file

@ -10,6 +10,18 @@
#include "ste_dma40_ll.h"
u8 d40_width_to_bits(enum dma_slave_buswidth width)
{
if (width == DMA_SLAVE_BUSWIDTH_1_BYTE)
return STEDMA40_ESIZE_8_BIT;
else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES)
return STEDMA40_ESIZE_16_BIT;
else if (width == DMA_SLAVE_BUSWIDTH_8_BYTES)
return STEDMA40_ESIZE_64_BIT;
else
return STEDMA40_ESIZE_32_BIT;
}
/* Sets up proper LCSP1 and LCSP3 register for a logical channel */
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
u32 *lcsp1, u32 *lcsp3)
@ -18,106 +30,100 @@ void d40_log_cfg(struct stedma40_chan_cfg *cfg,
u32 l1 = 0; /* src */
/* src is mem? -> increase address pos */
if (cfg->dir == STEDMA40_MEM_TO_PERIPH ||
cfg->dir == STEDMA40_MEM_TO_MEM)
l1 |= 1 << D40_MEM_LCSP1_SCFG_INCR_POS;
if (cfg->dir == DMA_MEM_TO_DEV ||
cfg->dir == DMA_MEM_TO_MEM)
l1 |= BIT(D40_MEM_LCSP1_SCFG_INCR_POS);
/* dst is mem? -> increase address pos */
if (cfg->dir == STEDMA40_PERIPH_TO_MEM ||
cfg->dir == STEDMA40_MEM_TO_MEM)
l3 |= 1 << D40_MEM_LCSP3_DCFG_INCR_POS;
if (cfg->dir == DMA_DEV_TO_MEM ||
cfg->dir == DMA_MEM_TO_MEM)
l3 |= BIT(D40_MEM_LCSP3_DCFG_INCR_POS);
/* src is hw? -> master port 1 */
if (cfg->dir == STEDMA40_PERIPH_TO_MEM ||
cfg->dir == STEDMA40_PERIPH_TO_PERIPH)
l1 |= 1 << D40_MEM_LCSP1_SCFG_MST_POS;
if (cfg->dir == DMA_DEV_TO_MEM ||
cfg->dir == DMA_DEV_TO_DEV)
l1 |= BIT(D40_MEM_LCSP1_SCFG_MST_POS);
/* dst is hw? -> master port 1 */
if (cfg->dir == STEDMA40_MEM_TO_PERIPH ||
cfg->dir == STEDMA40_PERIPH_TO_PERIPH)
l3 |= 1 << D40_MEM_LCSP3_DCFG_MST_POS;
if (cfg->dir == DMA_MEM_TO_DEV ||
cfg->dir == DMA_DEV_TO_DEV)
l3 |= BIT(D40_MEM_LCSP3_DCFG_MST_POS);
l3 |= 1 << D40_MEM_LCSP3_DCFG_EIM_POS;
l3 |= BIT(D40_MEM_LCSP3_DCFG_EIM_POS);
l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS;
l3 |= cfg->dst_info.data_width << D40_MEM_LCSP3_DCFG_ESIZE_POS;
l3 |= d40_width_to_bits(cfg->dst_info.data_width)
<< D40_MEM_LCSP3_DCFG_ESIZE_POS;
l1 |= 1 << D40_MEM_LCSP1_SCFG_EIM_POS;
l1 |= BIT(D40_MEM_LCSP1_SCFG_EIM_POS);
l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
l1 |= cfg->src_info.data_width << D40_MEM_LCSP1_SCFG_ESIZE_POS;
l1 |= d40_width_to_bits(cfg->src_info.data_width)
<< D40_MEM_LCSP1_SCFG_ESIZE_POS;
*lcsp1 = l1;
*lcsp3 = l3;
}
/* Sets up SRC and DST CFG register for both logical and physical channels */
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
u32 *src_cfg, u32 *dst_cfg, bool is_log)
void d40_phy_cfg(struct stedma40_chan_cfg *cfg, u32 *src_cfg, u32 *dst_cfg)
{
u32 src = 0;
u32 dst = 0;
if (!is_log) {
/* Physical channel */
if ((cfg->dir == STEDMA40_PERIPH_TO_MEM) ||
(cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) {
/* Set master port to 1 */
src |= 1 << D40_SREG_CFG_MST_POS;
src |= D40_TYPE_TO_EVENT(cfg->src_dev_type);
if ((cfg->dir == DMA_DEV_TO_MEM) ||
(cfg->dir == DMA_DEV_TO_DEV)) {
/* Set master port to 1 */
src |= BIT(D40_SREG_CFG_MST_POS);
src |= D40_TYPE_TO_EVENT(cfg->dev_type);
if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
src |= 1 << D40_SREG_CFG_PHY_TM_POS;
else
src |= 3 << D40_SREG_CFG_PHY_TM_POS;
}
if ((cfg->dir == STEDMA40_MEM_TO_PERIPH) ||
(cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) {
/* Set master port to 1 */
dst |= 1 << D40_SREG_CFG_MST_POS;
dst |= D40_TYPE_TO_EVENT(cfg->dst_dev_type);
if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
src |= BIT(D40_SREG_CFG_PHY_TM_POS);
else
src |= 3 << D40_SREG_CFG_PHY_TM_POS;
}
if ((cfg->dir == DMA_MEM_TO_DEV) ||
(cfg->dir == DMA_DEV_TO_DEV)) {
/* Set master port to 1 */
dst |= BIT(D40_SREG_CFG_MST_POS);
dst |= D40_TYPE_TO_EVENT(cfg->dev_type);
if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
dst |= 1 << D40_SREG_CFG_PHY_TM_POS;
else
dst |= 3 << D40_SREG_CFG_PHY_TM_POS;
}
/* Interrupt on end of transfer for destination */
dst |= 1 << D40_SREG_CFG_TIM_POS;
if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL)
dst |= BIT(D40_SREG_CFG_PHY_TM_POS);
else
dst |= 3 << D40_SREG_CFG_PHY_TM_POS;
}
/* Interrupt on end of transfer for destination */
dst |= BIT(D40_SREG_CFG_TIM_POS);
/* Generate interrupt on error */
src |= 1 << D40_SREG_CFG_EIM_POS;
dst |= 1 << D40_SREG_CFG_EIM_POS;
/* Generate interrupt on error */
src |= BIT(D40_SREG_CFG_EIM_POS);
dst |= BIT(D40_SREG_CFG_EIM_POS);
/* PSIZE */
if (cfg->src_info.psize != STEDMA40_PSIZE_PHY_1) {
src |= 1 << D40_SREG_CFG_PHY_PEN_POS;
src |= cfg->src_info.psize << D40_SREG_CFG_PSIZE_POS;
}
if (cfg->dst_info.psize != STEDMA40_PSIZE_PHY_1) {
dst |= 1 << D40_SREG_CFG_PHY_PEN_POS;
dst |= cfg->dst_info.psize << D40_SREG_CFG_PSIZE_POS;
}
/* PSIZE */
if (cfg->src_info.psize != STEDMA40_PSIZE_PHY_1) {
src |= BIT(D40_SREG_CFG_PHY_PEN_POS);
src |= cfg->src_info.psize << D40_SREG_CFG_PSIZE_POS;
}
if (cfg->dst_info.psize != STEDMA40_PSIZE_PHY_1) {
dst |= BIT(D40_SREG_CFG_PHY_PEN_POS);
dst |= cfg->dst_info.psize << D40_SREG_CFG_PSIZE_POS;
}
/* Element size */
src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS;
dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS;
/* Element size */
src |= d40_width_to_bits(cfg->src_info.data_width)
<< D40_SREG_CFG_ESIZE_POS;
dst |= d40_width_to_bits(cfg->dst_info.data_width)
<< D40_SREG_CFG_ESIZE_POS;
/* Set the priority bit to high for the physical channel */
if (cfg->high_priority) {
src |= 1 << D40_SREG_CFG_PRI_POS;
dst |= 1 << D40_SREG_CFG_PRI_POS;
}
} else {
/* Logical channel */
dst |= 1 << D40_SREG_CFG_LOG_GIM_POS;
src |= 1 << D40_SREG_CFG_LOG_GIM_POS;
/* Set the priority bit to high for the physical channel */
if (cfg->high_priority) {
src |= BIT(D40_SREG_CFG_PRI_POS);
dst |= BIT(D40_SREG_CFG_PRI_POS);
}
if (cfg->src_info.big_endian)
src |= 1 << D40_SREG_CFG_LBE_POS;
src |= BIT(D40_SREG_CFG_LBE_POS);
if (cfg->dst_info.big_endian)
dst |= 1 << D40_SREG_CFG_LBE_POS;
dst |= BIT(D40_SREG_CFG_LBE_POS);
*src_cfg = src;
*dst_cfg = dst;
@ -143,23 +149,22 @@ static int d40_phy_fill_lli(struct d40_phy_lli *lli,
num_elems = 2 << psize;
/* Must be aligned */
if (!IS_ALIGNED(data, 0x1 << data_width))
if (!IS_ALIGNED(data, data_width))
return -EINVAL;
/* Transfer size can't be smaller than (num_elms * elem_size) */
if (data_size < num_elems * (0x1 << data_width))
if (data_size < num_elems * data_width)
return -EINVAL;
/* The number of elements. IE now many chunks */
lli->reg_elt = (data_size >> data_width) << D40_SREG_ELEM_PHY_ECNT_POS;
lli->reg_elt = (data_size / data_width) << D40_SREG_ELEM_PHY_ECNT_POS;
/*
* Distance to next element sized entry.
* Usually the size of the element unless you want gaps.
*/
if (addr_inc)
lli->reg_elt |= (0x1 << data_width) <<
D40_SREG_ELEM_PHY_EIDX_POS;
lli->reg_elt |= data_width << D40_SREG_ELEM_PHY_EIDX_POS;
/* Where the data is */
lli->reg_ptr = data;
@ -167,18 +172,20 @@ static int d40_phy_fill_lli(struct d40_phy_lli *lli,
/* If this scatter list entry is the last one, no next link */
if (next_lli == 0)
lli->reg_lnk = 0x1 << D40_SREG_LNK_PHY_TCP_POS;
lli->reg_lnk = BIT(D40_SREG_LNK_PHY_TCP_POS);
else
lli->reg_lnk = next_lli;
/* Set/clear interrupt generation on this link item.*/
if (term_int)
lli->reg_cfg |= 0x1 << D40_SREG_CFG_TIM_POS;
lli->reg_cfg |= BIT(D40_SREG_CFG_TIM_POS);
else
lli->reg_cfg &= ~(0x1 << D40_SREG_CFG_TIM_POS);
lli->reg_cfg &= ~BIT(D40_SREG_CFG_TIM_POS);
/* Post link */
lli->reg_lnk |= 0 << D40_SREG_LNK_PHY_PRE_POS;
/*
* Post link - D40_SREG_LNK_PHY_PRE_POS = 0
* Relink happens after transfer completion.
*/
return 0;
}
@ -187,16 +194,16 @@ static int d40_seg_size(int size, int data_width1, int data_width2)
{
u32 max_w = max(data_width1, data_width2);
u32 min_w = min(data_width1, data_width2);
u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);
u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w);
if (seg_max > STEDMA40_MAX_SEG_SIZE)
seg_max -= (1 << max_w);
seg_max -= max_w;
if (size <= seg_max)
return size;
if (size <= 2 * seg_max)
return ALIGN(size / 2, 1 << max_w);
return ALIGN(size / 2, max_w);
return seg_max;
}
@ -362,10 +369,10 @@ static void d40_log_fill_lli(struct d40_log_lli *lli,
lli->lcsp13 = reg_cfg;
/* The number of elements to transfer */
lli->lcsp02 = ((data_size >> data_width) <<
lli->lcsp02 = ((data_size / data_width) <<
D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK;
BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE);
BUG_ON((data_size / data_width) > STEDMA40_MAX_SEG_SIZE);
/* 16 LSBs address of the current element */
lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK;

View file

@ -432,8 +432,7 @@ enum d40_lli_flags {
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
u32 *src_cfg,
u32 *dst_cfg,
bool is_log);
u32 *dst_cfg);
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
u32 *lcsp1,