* [PATCH 2/2] mmc: dw_mmc: k3: remove clk_table
From: Seungwon Jeon @ 2014-01-17 14:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389604469-8064-3-git-send-email-zhangfei.gao@linaro.org>
+ Chris Ball (New e-mail)
On Tue, January 14, 2014, Seungwon Jeon wrote:
> On Monday, January 13, 2014, Zhangfei Gao wrote:
> > Remove clk_table and directly use ios->clock as clock source rate.
> > Abstract init clock rate and max clock limitation in clk.c
> >
> > Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>
> Acked-by: Seungwon Jeon <tgih.jun@samsung.com>
>
> Thanks,
> Seungwon Jeon
^ permalink raw reply
* [PATCH v16] dmaengine: Add MOXA ART DMA engine driver
From: Arnd Bergmann @ 2014-01-17 14:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389948365-13999-1-git-send-email-jonas.jensen@gmail.com>
On Friday 17 January 2014, Jonas Jensen wrote:
> The MOXA ART SoC has a DMA controller capable of offloading expensive
> memory operations, such as large copies. This patch adds support for
> the controller including four channels. Two of these are used to
> handle MMC copy on the UC-7112-LX hardware. The remaining two can be
> used in a future audio driver or client application.
>
> Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>
> ---
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* [PATCH v6] mmc: sdhci-moxart: Add MOXA ART SDHCI driver
From: Arnd Bergmann @ 2014-01-17 14:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389951825-14884-1-git-send-email-jonas.jensen@gmail.com>
On Friday 17 January 2014, Jonas Jensen wrote:
> Add SDHCI driver for MOXA ART SoCs.
>
> Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>
I think this should be renamed to something other than SDHCI, since that
implies a specific register layout and would use the sdhci.c driver.
Maybe moxart-mmc?
> diff --git a/Documentation/devicetree/bindings/mmc/moxa,moxart-sdhci.txt b/Documentation/devicetree/bindings/mmc/moxa,moxart-sdhci.txt
> new file mode 100644
> index 0000000..020b13e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/moxa,moxart-sdhci.txt
> @@ -0,0 +1,28 @@
> +MOXA ART SD Host Controller Interface
> +
> +Required properties:
> +
> +- compatible : Must be "moxa,moxart-sdhci"
> +- reg : Should contain registers location and length
> +- interrupts : Should contain the interrupt number
> +- clocks : Should contain phandle for the clock feeding the SDHCI controller
> +
> +Optional properties:
> +
> +These are optional but required to enable DMA transfer mode:
> +
> +- dmas : Should contain two DMA channels, line request number must be 5 for
> + both channels
> +- dma-names : Must be "tx", "rx"
I think you should add a reference to bindings/mmc/mmc.txt here.
> +#define MSD_CMD_REG 0
> +#define MSD_ARG_REG 4
> +#define MSD_RESP0_REG 8
> +#define MSD_RESP1_REG 0x0c
> +#define MSD_RESP2_REG 0x10
> +#define MSD_RESP3_REG 0x14
> +#define MSD_RESP_CMD_REG 0x18
> +#define MSD_DATA_CTRL_REG 0x1c
> +#define MSD_DATA_TIMER_REG 0x20
> +#define MSD_DATA_LEN_REG 0x24
> +#define MSD_STATUS_REG 0x28
> +#define MSD_CLEAR_REG 0x2c
> +#define MSD_INT_MASK_REG 0x30
> +#define MSD_POWER_CTRL_REG 0x34
> +#define MSD_CLOCK_CTRL_REG 0x38
> +#define MSD_BUS_WIDTH_REG 0x3c
> +#define MSD_DATA_WIN_REG 0x40
> +#define MSD_FEATURE_REG 0x44
> +#define MSD_REVISION_REG 0x48
> +
> +#define MMC_RSP_SHORT 1
> +#define MMC_RSP_LONG 2
> +#define MMC_RSP_MASK 3
> +#define MMC_ERR_NONE 0
> +#define MMC_ERR_TIMEOUT 1
> +#define MMC_MODE_MMC 0
> +#define MMC_MODE_SD 1
> +#define MMC_ERR_BADCRC 2
> +#define MMC_VDD_360 23
> +
> +#define MSD_RETRY_COUNT 10
> +
> +#define REG_COMMAND 0
> +#define REG_ARGUMENT 4
> +#define REG_RESPONSE0 8
> +#define REG_RESPONSE1 12
> +#define REG_RESPONSE2 16
> +#define REG_RESPONSE3 20
> +#define REG_RESPONSE_COMMAND 24
> +#define REG_DATA_CONTROL 28
> +#define REG_DATA_TIMER 32
> +#define REG_DATA_LENGTH 36
> +#define REG_STATUS 40
> +#define REG_CLEAR 44
> +#define REG_INTERRUPT_MASK 48
> +#define REG_POWER_CONTROL 52
> +#define REG_CLOCK_CONTROL 56
> +#define REG_BUS_WIDTH 60
> +#define REG_DATA_WINDOW 64
> +#define REG_FEATURE 68
> +#define REG_REVISION 72
The lists seem duplicated here, there is an MSD_foo_REG for each REG_foo.
> + /*
> + * hardware does not support MMC_CAP_SD_HIGHSPEED
> + * CMD6 will timeout and make things not work
> + */
> + mmc->caps = MMC_CAP_4_BIT_DATA;
Better get the bus-width from DT by calling the mmc_of_parse
function. Some boards might connect only one data bit, or in
fact 8 if it's an eMMC.
Arnd
^ permalink raw reply
* [PATCH] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Philip Balister @ 2014-01-17 14:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389894803-4147-2-git-send-email-sthokal@xilinx.com>
On 01/16/2014 12:53 PM, Srikanth Thokala wrote:
> This is the driver for the AXI Video Direct Memory Access (AXI
> VDMA) core, which is a soft Xilinx IP core that provides high-
> bandwidth direct memory access between memory and AXI4-Stream
> type video target peripherals. The core provides efficient two
> dimensional DMA operations with independent asynchronous read
> and write channel operation.
>
[snip]
> +/**
> + * xilinx_vdma_start - Start VDMA channel
> + * @chan: Driver specific VDMA channel
> + */
> +static void xilinx_vdma_start(struct xilinx_vdma_chan *chan)
> +{
> + int loop = XILINX_VDMA_LOOP_COUNT + 1;
> +
> + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP);
> +
> + /* Wait for the hardware to start */
> + while (loop)
loop-- ?
Philip
> + if (!(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
> + XILINX_VDMA_DMASR_HALTED))
> + break;
> +
> + if (!loop) {
> + dev_err(chan->dev, "Cannot start channel %p: %x\n",
> + chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
> +
> + chan->err = true;
> + }
> +
> + return;
> +}
> +
> +/**
> + * xilinx_vdma_start_transfer - Starts VDMA transfer
> + * @chan: Driver specific channel struct pointer
> + */
> +static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
> +{
> + struct xilinx_vdma_config *config = &chan->config;
> + struct xilinx_vdma_tx_descriptor *desc;
> + unsigned long flags;
> + u32 reg;
> + struct xilinx_vdma_tx_segment *head, *tail = NULL;
> +
> + if (chan->err)
> + return;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> +
> + /* There's already an active descriptor, bail out. */
> + if (chan->active_desc)
> + goto out_unlock;
> +
> + if (list_empty(&chan->pending_list))
> + goto out_unlock;
> +
> + desc = list_first_entry(&chan->pending_list,
> + struct xilinx_vdma_tx_descriptor, node);
> +
> + /* If it is SG mode and hardware is busy, cannot submit */
> + if (chan->has_sg && xilinx_vdma_is_running(chan) &&
> + !xilinx_vdma_is_idle(chan)) {
> + dev_dbg(chan->dev, "DMA controller still busy\n");
> + goto out_unlock;
> + }
> +
> + if (chan->err)
> + goto out_unlock;
> +
> + /*
> + * If hardware is idle, then all descriptors on the running lists are
> + * done, start new transfers
> + */
> + if (chan->has_sg) {
> + head = list_first_entry(&desc->segments,
> + struct xilinx_vdma_tx_segment, node);
> + tail = list_entry(desc->segments.prev,
> + struct xilinx_vdma_tx_segment, node);
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_CURDESC, head->phys);
> + }
> +
> + /* Configure the hardware using info in the config structure */
> + reg = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
> +
> + if (config->frm_cnt_en)
> + reg |= XILINX_VDMA_DMACR_FRAMECNT_EN;
> + else
> + reg &= ~XILINX_VDMA_DMACR_FRAMECNT_EN;
> +
> + /*
> + * With SG, start with circular mode, so that BDs can be fetched.
> + * In direct register mode, if not parking, enable circular mode
> + */
> + if (chan->has_sg || !config->park)
> + reg |= XILINX_VDMA_DMACR_CIRC_EN;
> +
> + if (config->park)
> + reg &= ~XILINX_VDMA_DMACR_CIRC_EN;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, reg);
> +
> + if (config->park && (config->park_frm >= 0) &&
> + (config->park_frm < chan->num_frms)) {
> + if (chan->direction == DMA_MEM_TO_DEV)
> + vdma_write(chan, XILINX_VDMA_REG_PARK_PTR,
> + config->park_frm <<
> + XILINX_VDMA_PARK_PTR_RD_REF_SHIFT);
> + else
> + vdma_write(chan, XILINX_VDMA_REG_PARK_PTR,
> + config->park_frm <<
> + XILINX_VDMA_PARK_PTR_WR_REF_SHIFT);
> + }
> +
> + /* Start the hardware */
> + xilinx_vdma_start(chan);
> +
> + if (chan->err)
> + goto out_unlock;
> +
> + /* Start the transfer */
> + if (chan->has_sg) {
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_TAILDESC, tail->phys);
> + } else {
> + struct xilinx_vdma_tx_segment *segment;
> + int i = 0;
> +
> + list_for_each_entry(segment, &desc->segments, node)
> + vdma_desc_write(chan,
> + XILINX_VDMA_REG_START_ADDRESS(i++),
> + segment->hw.buf_addr);
> +
> + vdma_desc_write(chan, XILINX_VDMA_REG_HSIZE, config->hsize);
> + vdma_desc_write(chan, XILINX_VDMA_REG_FRMDLY_STRIDE,
> + (config->frm_dly <<
> + XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT) |
> + (config->stride <<
> + XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT));
> + vdma_desc_write(chan, XILINX_VDMA_REG_VSIZE, config->vsize);
> + }
> +
> + list_del(&desc->node);
> + chan->active_desc = desc;
> +
> +out_unlock:
> + spin_unlock_irqrestore(&chan->lock, flags);
> +}
> +
> +/**
> + * xilinx_vdma_issue_pending - Issue pending transactions
> + * @dchan: DMA channel
> + */
> +static void xilinx_vdma_issue_pending(struct dma_chan *dchan)
> +{
> + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
> +
> + xilinx_vdma_start_transfer(chan);
> +}
> +
> +/**
> + * xilinx_vdma_complete_descriptor - Mark the active descriptor as complete
> + * @chan : xilinx DMA channel
> + *
> + * CONTEXT: hardirq
> + */
> +static void xilinx_vdma_complete_descriptor(struct xilinx_vdma_chan *chan)
> +{
> + struct xilinx_vdma_tx_descriptor *desc;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> +
> + desc = chan->active_desc;
> + if (!desc) {
> + dev_dbg(chan->dev, "no running descriptors\n");
> + goto out_unlock;
> + }
> +
> + list_add_tail(&desc->node, &chan->done_list);
> +
> + /* Update the completed cookie and reset the active descriptor. */
> + chan->completed_cookie = desc->async_tx.cookie;
> + chan->active_desc = NULL;
> +
> +out_unlock:
> + spin_unlock_irqrestore(&chan->lock, flags);
> +}
> +
> +/**
> + * xilinx_vdma_reset - Reset VDMA channel
> + * @chan: Driver specific VDMA channel
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_reset(struct xilinx_vdma_chan *chan)
> +{
> + int loop = XILINX_VDMA_LOOP_COUNT + 1;
> + u32 tmp;
> +
> + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RESET);
> +
> + tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
> + XILINX_VDMA_DMACR_RESET;
> +
> + /* Wait for the hardware to finish reset */
> + while (loop-- && tmp)
> + tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
> + XILINX_VDMA_DMACR_RESET;
> +
> + if (!loop) {
> + dev_err(chan->dev, "reset timeout, cr %x, sr %x\n",
> + vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR),
> + vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
> + return -ETIMEDOUT;
> + }
> +
> + chan->err = false;
> +
> + return 0;
> +}
> +
> +/**
> + * xilinx_vdma_chan_reset - Reset VDMA channel and enable interrupts
> + * @chan: Driver specific VDMA channel
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_chan_reset(struct xilinx_vdma_chan *chan)
> +{
> + int err;
> +
> + /* Reset VDMA */
> + err = xilinx_vdma_reset(chan);
> + if (err)
> + return err;
> +
> + /* Enable interrupts */
> + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR,
> + XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
> +
> + return 0;
> +}
> +
> +/**
> + * xilinx_vdma_irq_handler - VDMA Interrupt handler
> + * @irq: IRQ number
> + * @data: Pointer to the Xilinx VDMA channel structure
> + *
> + * Return: IRQ_HANDLED/IRQ_NONE
> + */
> +static irqreturn_t xilinx_vdma_irq_handler(int irq, void *data)
> +{
> + struct xilinx_vdma_chan *chan = data;
> + u32 status;
> +
> + /* Read the status and ack the interrupts. */
> + status = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR);
> + if (!(status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK))
> + return IRQ_NONE;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR,
> + status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
> +
> + if (status & XILINX_VDMA_DMASR_ERR_IRQ) {
> + /*
> + * An error occurred. If C_FLUSH_ON_FSYNC is enabled and the
> + * error is recoverable, ignore it. Otherwise flag the error.
> + *
> + * Only recoverable errors can be cleared in the DMASR register,
> + * make sure not to write to other error bits to 1.
> + */
> + u32 errors = status & XILINX_VDMA_DMASR_ALL_ERR_MASK;
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR,
> + errors & XILINX_VDMA_DMASR_ERR_RECOVER_MASK);
> +
> + if (!chan->flush_on_fsync ||
> + (errors & ~XILINX_VDMA_DMASR_ERR_RECOVER_MASK)) {
> + dev_err(chan->dev,
> + "Channel %p has errors %x, cdr %x tdr %x\n",
> + chan, errors,
> + vdma_ctrl_read(chan, XILINX_VDMA_REG_CURDESC),
> + vdma_ctrl_read(chan, XILINX_VDMA_REG_TAILDESC));
> + chan->err = true;
> + }
> + }
> +
> + if (status & XILINX_VDMA_DMASR_DLY_CNT_IRQ) {
> + /*
> + * Device takes too long to do the transfer when user requires
> + * responsiveness.
> + */
> + dev_dbg(chan->dev, "Inter-packet latency too long\n");
> + }
> +
> + if (status & XILINX_VDMA_DMASR_FRM_CNT_IRQ) {
> + xilinx_vdma_complete_descriptor(chan);
> + xilinx_vdma_start_transfer(chan);
> + }
> +
> + tasklet_schedule(&chan->tasklet);
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * xilinx_vdma_tx_submit - Submit DMA transaction
> + * @tx: Async transaction descriptor
> + *
> + * Return: cookie value on success and failure value on error
> + */
> +static dma_cookie_t xilinx_vdma_tx_submit(struct dma_async_tx_descriptor *tx)
> +{
> + struct xilinx_vdma_tx_descriptor *desc = to_vdma_tx_descriptor(tx);
> + struct xilinx_vdma_chan *chan = to_xilinx_chan(tx->chan);
> + struct xilinx_vdma_tx_segment *segment;
> + dma_cookie_t cookie;
> + unsigned long flags;
> + int err;
> +
> + if (chan->err) {
> + /*
> + * If reset fails, need to hard reset the system.
> + * Channel is no longer functional
> + */
> + err = xilinx_vdma_chan_reset(chan);
> + if (err < 0)
> + return err;
> + }
> +
> + spin_lock_irqsave(&chan->lock, flags);
> +
> + /* Assign cookies to all of the segments that make up this transaction.
> + * Use the cookie of the last segment as the transaction cookie.
> + */
> + cookie = chan->cookie;
> +
> + list_for_each_entry(segment, &desc->segments, node) {
> + if (cookie < DMA_MAX_COOKIE)
> + cookie++;
> + else
> + cookie = DMA_MIN_COOKIE;
> +
> + segment->cookie = cookie;
> + }
> +
> + tx->cookie = cookie;
> + chan->cookie = cookie;
> +
> + /* Append the transaction to the pending transactions queue. */
> + list_add_tail(&desc->node, &chan->pending_list);
> +
> + spin_unlock_irqrestore(&chan->lock, flags);
> +
> + return cookie;
> +}
> +
> +/**
> + * xilinx_vdma_prep_slave_sg - prepare a descriptor for a DMA_SLAVE transaction
> + * @dchan: DMA channel
> + * @sgl: scatterlist to transfer to/from
> + * @sg_len: number of entries in @sgl
> + * @dir: DMA direction
> + * @flags: transfer ack flags
> + * @context: unused
> + *
> + * Return: Async transaction descriptor on success and NULL on failure
> + */
> +static struct dma_async_tx_descriptor *
> +xilinx_vdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction dir,
> + unsigned long flags, void *context)
> +{
> + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
> + struct xilinx_vdma_tx_descriptor *desc;
> + struct xilinx_vdma_tx_segment *segment;
> + struct xilinx_vdma_tx_segment *prev = NULL;
> + struct scatterlist *sg;
> + int i;
> +
> + if (chan->direction != dir || sg_len == 0)
> + return NULL;
> +
> + /* Enforce one sg entry for one frame. */
> + if (sg_len != chan->num_frms) {
> + dev_err(chan->dev,
> + "number of entries %d not the same as num stores %d\n",
> + sg_len, chan->num_frms);
> + return NULL;
> + }
> +
> + /* Allocate a transaction descriptor. */
> + desc = xilinx_vdma_alloc_tx_descriptor(chan);
> + if (!desc)
> + return NULL;
> +
> + dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
> + desc->async_tx.tx_submit = xilinx_vdma_tx_submit;
> + desc->async_tx.cookie = 0;
> + async_tx_ack(&desc->async_tx);
> +
> + /* Build the list of transaction segments. */
> + for_each_sg(sgl, sg, sg_len, i) {
> + struct xilinx_vdma_desc_hw *hw;
> +
> + /* Allocate the link descriptor from DMA pool */
> + segment = xilinx_vdma_alloc_tx_segment(chan);
> + if (!segment)
> + goto error;
> +
> + /* Fill in the hardware descriptor */
> + hw = &segment->hw;
> + hw->buf_addr = sg_dma_address(sg);
> + hw->vsize = chan->config.vsize;
> + hw->hsize = chan->config.hsize;
> + hw->stride = (chan->config.frm_dly <<
> + XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT) |
> + (chan->config.stride <<
> + XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT);
> + if (prev)
> + prev->hw.next_desc = segment->phys;
> +
> + /* Insert the segment into the descriptor segments list. */
> + list_add_tail(&segment->node, &desc->segments);
> +
> + prev = segment;
> + }
> +
> + /* Link the last hardware descriptor with the first. */
> + segment = list_first_entry(&desc->segments,
> + struct xilinx_vdma_tx_segment, node);
> + prev->hw.next_desc = segment->phys;
> +
> + return &desc->async_tx;
> +
> +error:
> + xilinx_vdma_free_tx_descriptor(chan, desc);
> + return NULL;
> +}
> +
> +/**
> + * xilinx_vdma_terminate_all - Halt the channel and free descriptors
> + * @chan: Driver specific VDMA Channel pointer
> + */
> +static void xilinx_vdma_terminate_all(struct xilinx_vdma_chan *chan)
> +{
> + /* Halt the DMA engine */
> + xilinx_vdma_halt(chan);
> +
> + /* Remove and free all of the descriptors in the lists */
> + xilinx_vdma_free_descriptors(chan);
> +}
> +
> +/**
> + * xilinx_vdma_slave_config - Configure VDMA channel
> + * Run-time configuration for Axi VDMA, supports:
> + * . halt the channel
> + * . configure interrupt coalescing and inter-packet delay threshold
> + * . start/stop parking
> + * . enable genlock
> + * . set transfer information using config struct
> + *
> + * @chan: Driver specific VDMA Channel pointer
> + * @cfg: Channel configuration pointer
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_slave_config(struct xilinx_vdma_chan *chan,
> + struct xilinx_vdma_config *cfg)
> +{
> + u32 dmacr;
> +
> + if (cfg->reset)
> + return xilinx_vdma_chan_reset(chan);
> +
> + dmacr = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
> +
> + /* If vsize is -1, it is park-related operations */
> + if (cfg->vsize == -1) {
> + if (cfg->park)
> + dmacr &= ~XILINX_VDMA_DMACR_CIRC_EN;
> + else
> + dmacr |= XILINX_VDMA_DMACR_CIRC_EN;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> + }
> +
> + /* If hsize is -1, it is interrupt threshold settings */
> + if (cfg->hsize == -1) {
> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
> + dmacr &= ~XILINX_VDMA_DMACR_FRAME_COUNT_MASK;
> + dmacr |= cfg->coalesc <<
> + XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
> + chan->config.coalesc = cfg->coalesc;
> + }
> +
> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
> + dmacr &= ~XILINX_VDMA_DMACR_DELAY_MASK;
> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
> + chan->config.delay = cfg->delay;
> + }
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> + }
> +
> + /* Transfer information */
> + chan->config.vsize = cfg->vsize;
> + chan->config.hsize = cfg->hsize;
> + chan->config.stride = cfg->stride;
> + chan->config.frm_dly = cfg->frm_dly;
> + chan->config.park = cfg->park;
> +
> + /* genlock settings */
> + chan->config.gen_lock = cfg->gen_lock;
> + chan->config.master = cfg->master;
> +
> + if (cfg->gen_lock && chan->genlock) {
> + dmacr |= XILINX_VDMA_DMACR_GENLOCK_EN;
> + dmacr |= cfg->master << XILINX_VDMA_DMACR_MASTER_SHIFT;
> + }
> +
> + chan->config.frm_cnt_en = cfg->frm_cnt_en;
> + if (cfg->park)
> + chan->config.park_frm = cfg->park_frm;
> + else
> + chan->config.park_frm = -1;
> +
> + chan->config.coalesc = cfg->coalesc;
> + chan->config.delay = cfg->delay;
> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
> + dmacr |= cfg->coalesc << XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
> + chan->config.coalesc = cfg->coalesc;
> + }
> +
> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
> + chan->config.delay = cfg->delay;
> + }
> +
> + /* FSync Source selection */
> + dmacr &= ~XILINX_VDMA_DMACR_FSYNCSRC_MASK;
> + dmacr |= cfg->ext_fsync << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> +}
> +
> +/**
> + * xilinx_vdma_device_control - Configure DMA channel of the device
> + * @dchan: DMA Channel pointer
> + * @cmd: DMA control command
> + * @arg: Channel configuration
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_device_control(struct dma_chan *dchan,
> + enum dma_ctrl_cmd cmd, unsigned long arg)
> +{
> + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
> +
> + switch (cmd) {
> + case DMA_TERMINATE_ALL:
> + xilinx_vdma_terminate_all(chan);
> + return 0;
> + case DMA_SLAVE_CONFIG:
> + return xilinx_vdma_slave_config(chan,
> + (struct xilinx_vdma_config *)arg);
> + default:
> + return -ENXIO;
> + }
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Probe and remove
> + */
> +
> +/**
> + * xilinx_vdma_chan_remove - Per Channel remove function
> + * @chan: Driver specific VDMA channel
> + */
> +static void xilinx_vdma_chan_remove(struct xilinx_vdma_chan *chan)
> +{
> + /* Disable all interrupts */
> + vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR,
> + XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
> +
> + list_del(&chan->common.device_node);
> +}
> +
> +/**
> + * xilinx_vdma_chan_probe - Per Channel Probing
> + * It get channel features from the device tree entry and
> + * initialize special channel handling routines
> + *
> + * @xdev: Driver specific device structure
> + * @node: Device node
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_chan_probe(struct xilinx_vdma_device *xdev,
> + struct device_node *node)
> +{
> + struct xilinx_vdma_chan *chan;
> + bool has_dre = false;
> + u32 device_id;
> + u32 value;
> + int err;
> +
> + /* Allocate and initialize the channel structure */
> + chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL);
> + if (!chan)
> + return -ENOMEM;
> +
> + chan->dev = xdev->dev;
> + chan->xdev = xdev;
> + chan->has_sg = xdev->has_sg;
> +
> + spin_lock_init(&chan->lock);
> + INIT_LIST_HEAD(&chan->pending_list);
> + INIT_LIST_HEAD(&chan->done_list);
> +
> + /* Retrieve the channel properties from the device tree */
> + has_dre = of_property_read_bool(node, "xlnx,include-dre");
> +
> + chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode");
> +
> + err = of_property_read_u32(node, "xlnx,datawidth", &value);
> + if (!err) {
> + u32 width = value >> 3; /* Convert bits to bytes */
> +
> + /* If data width is greater than 8 bytes, DRE is not in hw */
> + if (width > 8)
> + has_dre = false;
> +
> + if (!has_dre)
> + xdev->common.copy_align = fls(width - 1);
> + }
> +
> + err = of_property_read_u32(node, "xlnx,device-id", &device_id);
> + if (err < 0) {
> + dev_err(xdev->dev, "missing xlnx,device-id property\n");
> + return err;
> + }
> +
> + if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel")) {
> + chan->direction = DMA_MEM_TO_DEV;
> + chan->id = 0;
> +
> + chan->ctrl_offset = XILINX_VDMA_MM2S_CTRL_OFFSET;
> + chan->desc_offset = XILINX_VDMA_MM2S_DESC_OFFSET;
> +
> + if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH ||
> + xdev->flush_on_fsync == XILINX_VDMA_FLUSH_MM2S)
> + chan->flush_on_fsync = true;
> + } else if (of_device_is_compatible(node,
> + "xlnx,axi-vdma-s2mm-channel")) {
> + chan->direction = DMA_DEV_TO_MEM;
> + chan->id = 1;
> +
> + chan->ctrl_offset = XILINX_VDMA_S2MM_CTRL_OFFSET;
> + chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET;
> +
> + if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH ||
> + xdev->flush_on_fsync == XILINX_VDMA_FLUSH_S2MM)
> + chan->flush_on_fsync = true;
> + } else {
> + dev_err(xdev->dev, "Invalid channel compatible node\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Used by DMA clients who doesnt have a device node and can request
> + * the channel by passing this as a filter to 'dma_request_channel()'.
> + */
> + chan->private = (chan->direction & 0xff) |
> + XILINX_DMA_IP_VDMA |
> + (device_id << XILINX_DMA_DEVICE_ID_SHIFT);
> +
> + /* Request the interrupt */
> + chan->irq = irq_of_parse_and_map(node, 0);
> + err = devm_request_irq(xdev->dev, chan->irq, xilinx_vdma_irq_handler,
> + IRQF_SHARED, "xilinx-vdma-controller", chan);
> + if (err) {
> + dev_err(xdev->dev, "unable to request IRQ\n");
> + return err;
> + }
> +
> + /* Initialize the DMA channel and add it to the DMA engine channels
> + * list.
> + */
> + chan->common.device = &xdev->common;
> + chan->common.private = (void *)&(chan->private);
> +
> + list_add_tail(&chan->common.device_node, &xdev->common.channels);
> + xdev->chan[chan->id] = chan;
> +
> + /* Reset the channel */
> + err = xilinx_vdma_chan_reset(chan);
> + if (err < 0) {
> + dev_err(xdev->dev, "Reset channel failed\n");
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * struct of_dma_filter_xilinx_args - Channel filter args
> + * @dev: DMA device structure
> + * @chan_id: Channel id
> + */
> +struct of_dma_filter_xilinx_args {
> + struct dma_device *dev;
> + u32 chan_id;
> +};
> +
> +/**
> + * xilinx_vdma_dt_filter - VDMA channel filter function
> + * @chan: DMA channel pointer
> + * @param: Filter match value
> + *
> + * Return: true/false based on the result
> + */
> +static bool xilinx_vdma_dt_filter(struct dma_chan *chan, void *param)
> +{
> + struct of_dma_filter_xilinx_args *args = param;
> +
> + return chan->device == args->dev && chan->chan_id == args->chan_id;
> +}
> +
> +/**
> + * of_dma_xilinx_xlate - Translation function
> + * @dma_spec: Pointer to DMA specifier as found in the device tree
> + * @ofdma: Pointer to DMA controller data
> + *
> + * Return: DMA channel pointer on success and NULL on error
> + */
> +static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec,
> + struct of_dma *ofdma)
> +{
> + struct of_dma_filter_xilinx_args args;
> + dma_cap_mask_t cap;
> +
> + args.dev = ofdma->of_dma_data;
> + if (!args.dev)
> + return NULL;
> +
> + if (dma_spec->args_count != 1)
> + return NULL;
> +
> + dma_cap_zero(cap);
> + dma_cap_set(DMA_SLAVE, cap);
> +
> + args.chan_id = dma_spec->args[0];
> +
> + return dma_request_channel(cap, xilinx_vdma_dt_filter, &args);
> +}
> +
> +/**
> + * xilinx_vdma_probe - Driver probe function
> + * @pdev: Pointer to the platform_device structure
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_probe(struct platform_device *pdev)
> +{
> + struct device_node *node = pdev->dev.of_node;
> + struct xilinx_vdma_device *xdev;
> + struct device_node *child;
> + struct resource *io;
> + u32 num_frames;
> + int i, err;
> +
> + dev_info(&pdev->dev, "Probing xilinx axi vdma engine\n");
> +
> + /* Allocate and initialize the DMA engine structure */
> + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
> + if (!xdev)
> + return -ENOMEM;
> +
> + xdev->dev = &pdev->dev;
> +
> + /* Request and map I/O memory */
> + io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + xdev->regs = devm_ioremap_resource(&pdev->dev, io);
> + if (IS_ERR(xdev->regs))
> + return PTR_ERR(xdev->regs);
> +
> + /* Retrieve the DMA engine properties from the device tree */
> + xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
> +
> + err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames);
> + if (err < 0) {
> + dev_err(xdev->dev, "missing xlnx,num-fstores property\n");
> + return err;
> + }
> +
> + of_property_read_u32(node, "xlnx,flush-fsync", &xdev->flush_on_fsync);
> +
> + /* Initialize the DMA engine */
> + xdev->common.dev = &pdev->dev;
> +
> + INIT_LIST_HEAD(&xdev->common.channels);
> + dma_cap_set(DMA_SLAVE, xdev->common.cap_mask);
> + dma_cap_set(DMA_PRIVATE, xdev->common.cap_mask);
> +
> + xdev->common.device_alloc_chan_resources =
> + xilinx_vdma_alloc_chan_resources;
> + xdev->common.device_free_chan_resources =
> + xilinx_vdma_free_chan_resources;
> + xdev->common.device_prep_slave_sg = xilinx_vdma_prep_slave_sg;
> + xdev->common.device_control = xilinx_vdma_device_control;
> + xdev->common.device_tx_status = xilinx_vdma_tx_status;
> + xdev->common.device_issue_pending = xilinx_vdma_issue_pending;
> +
> + platform_set_drvdata(pdev, xdev);
> +
> + /* Initialize the channels */
> + for_each_child_of_node(node, child) {
> + err = xilinx_vdma_chan_probe(xdev, child);
> + if (err < 0)
> + goto error;
> + }
> +
> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
> + if (xdev->chan[i])
> + xdev->chan[i]->num_frms = num_frames;
> + }
> +
> + /* Register the DMA engine with the core */
> + dma_async_device_register(&xdev->common);
> +
> + err = of_dma_controller_register(node, of_dma_xilinx_xlate,
> + &xdev->common);
> + if (err < 0)
> + dev_err(&pdev->dev, "Unable to register DMA to DT\n");
> +
> + return 0;
> +
> +error:
> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
> + if (xdev->chan[i])
> + xilinx_vdma_chan_remove(xdev->chan[i]);
> + }
> +
> + return err;
> +}
> +
> +/**
> + * xilinx_vdma_remove - Driver remove function
> + * @pdev: Pointer to the platform_device structure
> + *
> + * Return: Always '0'
> + */
> +static int xilinx_vdma_remove(struct platform_device *pdev)
> +{
> + struct xilinx_vdma_device *xdev;
> + int i;
> +
> + of_dma_controller_free(pdev->dev.of_node);
> +
> + xdev = platform_get_drvdata(pdev);
> + dma_async_device_unregister(&xdev->common);
> +
> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
> + if (xdev->chan[i])
> + xilinx_vdma_chan_remove(xdev->chan[i]);
> + }
> +
> + return 0;
> +}
> +
> +static const struct of_device_id xilinx_vdma_of_ids[] = {
> + { .compatible = "xlnx,axi-vdma-1.00.a",},
> + {}
> +};
> +
> +static struct platform_driver xilinx_vdma_driver = {
> + .driver = {
> + .name = "xilinx-vdma",
> + .owner = THIS_MODULE,
> + .of_match_table = xilinx_vdma_of_ids,
> + },
> + .probe = xilinx_vdma_probe,
> + .remove = xilinx_vdma_remove,
> +};
> +
> +module_platform_driver(xilinx_vdma_driver);
> +
> +MODULE_AUTHOR("Xilinx, Inc.");
> +MODULE_DESCRIPTION("Xilinx VDMA driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/xilinx/xilinx_vdma_test.c b/drivers/dma/xilinx/xilinx_vdma_test.c
> new file mode 100644
> index 0000000..813b67c
> --- /dev/null
> +++ b/drivers/dma/xilinx/xilinx_vdma_test.c
> @@ -0,0 +1,629 @@
> +/*
> + * XILINX VDMA Engine test client driver
> + *
> + * Copyright (C) 2010-2013 Xilinx, Inc. All rights reserved.
> + *
> + * Based on Atmel DMA Test Client
> + *
> + * Description:
> + * This is a simple Xilinx VDMA test client for AXI VDMA driver.
> + * This test assumes both the channels of VDMA are enabled in the
> + * hardware design and configured in back-to-back connection. Test
> + * starts by pumping the data onto one channel (MM2S) and then
> + * compares the data that is received on the other channel (S2MM).
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/amba/xilinx_dma.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/kthread.h>
> +#include <linux/module.h>
> +#include <linux/of_dma.h>
> +#include <linux/platform_device.h>
> +#include <linux/random.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +static unsigned int test_buf_size = 64;
> +module_param(test_buf_size, uint, S_IRUGO);
> +MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
> +
> +static unsigned int iterations;
> +module_param(iterations, uint, S_IRUGO);
> +MODULE_PARM_DESC(iterations,
> + "Iterations before stopping test (default: infinite)");
> +
> +/*
> + * Initialization patterns. All bytes in the source buffer has bit 7
> + * set, all bytes in the destination buffer has bit 7 cleared.
> + *
> + * Bit 6 is set for all bytes which are to be copied by the DMA
> + * engine. Bit 5 is set for all bytes which are to be overwritten by
> + * the DMA engine.
> + *
> + * The remaining bits are the inverse of a counter which increments by
> + * one for each byte address.
> + */
> +#define PATTERN_SRC 0x80
> +#define PATTERN_DST 0x00
> +#define PATTERN_COPY 0x40
> +#define PATTERN_OVERWRITE 0x20
> +#define PATTERN_COUNT_MASK 0x1f
> +
> +/* Maximum number of frame buffers */
> +#define MAX_NUM_FRAMES 32
> +
> +/**
> + * struct vdmatest_slave_thread - VDMA test thread
> + * @node: Thread node
> + * @task: Task structure pointer
> + * @tx_chan: Tx channel pointer
> + * @rx_chan: Rx Channel pointer
> + * @srcs: Source buffer
> + * @dsts: Destination buffer
> + * @type: DMA transaction type
> + */
> +struct xilinx_vdmatest_slave_thread {
> + struct list_head node;
> + struct task_struct *task;
> + struct dma_chan *tx_chan;
> + struct dma_chan *rx_chan;
> + u8 **srcs;
> + u8 **dsts;
> + enum dma_transaction_type type;
> +};
> +
> +/**
> + * struct vdmatest_chan - VDMA Test channel
> + * @node: Channel node
> + * @chan: DMA channel pointer
> + * @threads: List of VDMA test threads
> + */
> +struct xilinx_vdmatest_chan {
> + struct list_head node;
> + struct dma_chan *chan;
> + struct list_head threads;
> +};
> +
> +/* Global variables */
> +static LIST_HEAD(xilinx_vdmatest_channels);
> +static unsigned int nr_channels;
> +static unsigned int frm_cnt;
> +static dma_addr_t dma_srcs[MAX_NUM_FRAMES];
> +static dma_addr_t dma_dsts[MAX_NUM_FRAMES];
> +static struct scatterlist tx_sg[MAX_NUM_FRAMES];
> +static struct scatterlist rx_sg[MAX_NUM_FRAMES];
> +
> +static void xilinx_vdmatest_init_srcs(u8 **bufs, unsigned int start,
> + unsigned int len)
> +{
> + unsigned int i;
> + u8 *buf;
> +
> + for (; (buf = *bufs); bufs++) {
> + for (i = 0; i < start; i++)
> + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
> + for (; i < start + len; i++)
> + buf[i] = PATTERN_SRC | PATTERN_COPY
> + | (~i & PATTERN_COUNT_MASK);
> + for (; i < test_buf_size; i++)
> + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
> + buf++;
> + }
> +}
> +
> +static void xilinx_vdmatest_init_dsts(u8 **bufs, unsigned int start,
> + unsigned int len)
> +{
> + unsigned int i;
> + u8 *buf;
> +
> + for (; (buf = *bufs); bufs++) {
> + for (i = 0; i < start; i++)
> + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
> + for (; i < start + len; i++)
> + buf[i] = PATTERN_DST | PATTERN_OVERWRITE
> + | (~i & PATTERN_COUNT_MASK);
> + for (; i < test_buf_size; i++)
> + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
> + }
> +}
> +
> +static void xilinx_vdmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
> + unsigned int counter, bool is_srcbuf)
> +{
> + u8 diff = actual ^ pattern;
> + u8 expected = pattern | (~counter & PATTERN_COUNT_MASK);
> + const char *thread_name = current->comm;
> +
> + if (is_srcbuf)
> + pr_warn(
> + "%s: srcbuf[0x%x] overwritten! Expected %02x, got %02x\n",
> + thread_name, index, expected, actual);
> + else if ((pattern & PATTERN_COPY)
> + && (diff & (PATTERN_COPY | PATTERN_OVERWRITE)))
> + pr_warn(
> + "%s: dstbuf[0x%x] not copied! Expected %02x, got %02x\n",
> + thread_name, index, expected, actual);
> + else if (diff & PATTERN_SRC)
> + pr_warn(
> + "%s: dstbuf[0x%x] was copied! Expected %02x, got %02x\n",
> + thread_name, index, expected, actual);
> + else
> + pr_warn(
> + "%s: dstbuf[0x%x] mismatch! Expected %02x, got %02x\n",
> + thread_name, index, expected, actual);
> +}
> +
> +static unsigned int xilinx_vdmatest_verify(u8 **bufs, unsigned int start,
> + unsigned int end, unsigned int counter, u8 pattern,
> + bool is_srcbuf)
> +{
> + unsigned int i, error_count = 0;
> + u8 actual, expected, *buf;
> + unsigned int counter_orig = counter;
> +
> + for (; (buf = *bufs); bufs++) {
> + counter = counter_orig;
> + for (i = start; i < end; i++) {
> + actual = buf[i];
> + expected = pattern | (~counter & PATTERN_COUNT_MASK);
> + if (actual != expected) {
> + if (error_count < 32)
> + xilinx_vdmatest_mismatch(actual,
> + pattern, i,
> + counter, is_srcbuf);
> + error_count++;
> + }
> + counter++;
> + }
> + }
> +
> + if (error_count > 32)
> + pr_warn("%s: %u errors suppressed\n",
> + current->comm, error_count - 32);
> +
> + return error_count;
> +}
> +
> +static void xilinx_vdmatest_slave_tx_callback(void *completion)
> +{
> + pr_debug("Got tx callback\n");
> + complete(completion);
> +}
> +
> +static void xilinx_vdmatest_slave_rx_callback(void *completion)
> +{
> + pr_debug("Got rx callback\n");
> + complete(completion);
> +}
> +
> +/*
> + * Function for slave transfers
> + * Each thread requires 2 channels, one for transmit, and one for receive
> + */
> +static int xilinx_vdmatest_slave_func(void *data)
> +{
> + struct xilinx_vdmatest_slave_thread *thread = data;
> + struct dma_chan *tx_chan, *rx_chan;
> + const char *thread_name;
> + unsigned int len, error_count;
> + unsigned int failed_tests = 0, total_tests = 0;
> + dma_cookie_t tx_cookie, rx_cookie;
> + enum dma_status status;
> + enum dma_ctrl_flags flags;
> + int ret = -ENOMEM, i;
> + int hsize = 64, vsize = 32;
> + struct xilinx_vdma_config config;
> +
> + thread_name = current->comm;
> +
> + /* Limit testing scope here */
> + iterations = 1;
> + test_buf_size = hsize * vsize;
> +
> + /* This barrier ensures 'thread' is initialized and
> + * we get valid DMA channels
> + */
> + smp_rmb();
> + tx_chan = thread->tx_chan;
> + rx_chan = thread->rx_chan;
> +
> + thread->srcs = kcalloc(frm_cnt+1, sizeof(u8 *), GFP_KERNEL);
> + if (!thread->srcs)
> + goto err_srcs;
> + for (i = 0; i < frm_cnt; i++) {
> + thread->srcs[i] = kmalloc(test_buf_size, GFP_KERNEL);
> + if (!thread->srcs[i])
> + goto err_srcbuf;
> + }
> +
> + thread->dsts = kcalloc(frm_cnt+1, sizeof(u8 *), GFP_KERNEL);
> + if (!thread->dsts)
> + goto err_dsts;
> + for (i = 0; i < frm_cnt; i++) {
> + thread->dsts[i] = kmalloc(test_buf_size, GFP_KERNEL);
> + if (!thread->dsts[i])
> + goto err_dstbuf;
> + }
> +
> + set_user_nice(current, 10);
> +
> + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
> +
> + while (!kthread_should_stop()
> + && !(iterations && total_tests >= iterations)) {
> + struct dma_device *tx_dev = tx_chan->device;
> + struct dma_device *rx_dev = rx_chan->device;
> + struct dma_async_tx_descriptor *txd = NULL;
> + struct dma_async_tx_descriptor *rxd = NULL;
> + struct completion rx_cmp, tx_cmp;
> + unsigned long rx_tmo =
> + msecs_to_jiffies(30000); /* RX takes longer */
> + unsigned long tx_tmo = msecs_to_jiffies(30000);
> + u8 align = 0;
> +
> + total_tests++;
> +
> + /* honor larger alignment restrictions */
> + align = tx_dev->copy_align;
> + if (rx_dev->copy_align > align)
> + align = rx_dev->copy_align;
> +
> + if (1 << align > test_buf_size) {
> + pr_err("%u-byte buffer too small for %d-byte alignment\n",
> + test_buf_size, 1 << align);
> + break;
> + }
> +
> + len = test_buf_size;
> + xilinx_vdmatest_init_srcs(thread->srcs, 0, len);
> + xilinx_vdmatest_init_dsts(thread->dsts, 0, len);
> +
> + sg_init_table(tx_sg, frm_cnt);
> + sg_init_table(rx_sg, frm_cnt);
> +
> + for (i = 0; i < frm_cnt; i++) {
> + u8 *buf = thread->srcs[i];
> +
> + dma_srcs[i] = dma_map_single(tx_dev->dev, buf, len,
> + DMA_MEM_TO_DEV);
> + pr_debug("src buf %x dma %x\n", (unsigned int)buf,
> + (unsigned int)dma_srcs[i]);
> + sg_dma_address(&tx_sg[i]) = dma_srcs[i];
> + sg_dma_len(&tx_sg[i]) = len;
> + }
> +
> + for (i = 0; i < frm_cnt; i++) {
> + dma_dsts[i] = dma_map_single(rx_dev->dev,
> + thread->dsts[i],
> + test_buf_size,
> + DMA_DEV_TO_MEM);
> + pr_debug("dst %x dma %x\n",
> + (unsigned int)thread->dsts[i],
> + (unsigned int)dma_dsts[i]);
> + sg_dma_address(&rx_sg[i]) = dma_dsts[i];
> + sg_dma_len(&rx_sg[i]) = len;
> + }
> +
> + /* Zero out configuration */
> + memset(&config, 0, sizeof(struct xilinx_vdma_config));
> +
> + /* Set up hardware configuration information */
> + config.vsize = vsize;
> + config.hsize = hsize;
> + config.stride = hsize;
> + config.frm_cnt_en = 1;
> + config.coalesc = frm_cnt * 10;
> + config.park = 1;
> + tx_dev->device_control(tx_chan, DMA_SLAVE_CONFIG,
> + (unsigned long)&config);
> +
> + config.park = 0;
> + rx_dev->device_control(rx_chan, DMA_SLAVE_CONFIG,
> + (unsigned long)&config);
> +
> + rxd = rx_dev->device_prep_slave_sg(rx_chan, rx_sg, frm_cnt,
> + DMA_DEV_TO_MEM, flags, NULL);
> +
> + txd = tx_dev->device_prep_slave_sg(tx_chan, tx_sg, frm_cnt,
> + DMA_MEM_TO_DEV, flags, NULL);
> +
> + if (!rxd || !txd) {
> + for (i = 0; i < frm_cnt; i++)
> + dma_unmap_single(tx_dev->dev, dma_srcs[i], len,
> + DMA_MEM_TO_DEV);
> + for (i = 0; i < frm_cnt; i++)
> + dma_unmap_single(rx_dev->dev, dma_dsts[i],
> + test_buf_size,
> + DMA_DEV_TO_MEM);
> + pr_warn("%s: #%u: prep error with len=0x%x ",
> + thread_name, total_tests - 1, len);
> + msleep(100);
> + failed_tests++;
> + continue;
> + }
> +
> + init_completion(&rx_cmp);
> + rxd->callback = xilinx_vdmatest_slave_rx_callback;
> + rxd->callback_param = &rx_cmp;
> + rx_cookie = rxd->tx_submit(rxd);
> +
> + init_completion(&tx_cmp);
> + txd->callback = xilinx_vdmatest_slave_tx_callback;
> + txd->callback_param = &tx_cmp;
> + tx_cookie = txd->tx_submit(txd);
> +
> + if (dma_submit_error(rx_cookie) ||
> + dma_submit_error(tx_cookie)) {
> + pr_warn("%s: #%u: submit error %d/%d with len=0x%x ",
> + thread_name, total_tests - 1,
> + rx_cookie, tx_cookie, len);
> + msleep(100);
> + failed_tests++;
> + continue;
> + }
> + dma_async_issue_pending(tx_chan);
> + dma_async_issue_pending(rx_chan);
> +
> + tx_tmo = wait_for_completion_timeout(&tx_cmp, tx_tmo);
> +
> + status = dma_async_is_tx_complete(tx_chan, tx_cookie,
> + NULL, NULL);
> +
> + if (tx_tmo == 0) {
> + pr_warn("%s: #%u: tx test timed out\n",
> + thread_name, total_tests - 1);
> + failed_tests++;
> + continue;
> + } else if (status != DMA_COMPLETE) {
> + pr_warn(
> + "%s: #%u: tx got completion callback, ",
> + thread_name, total_tests - 1);
> + pr_warn("but status is \'%s\'\n",
> + status == DMA_ERROR ? "error" :
> + "in progress");
> + failed_tests++;
> + continue;
> + }
> +
> + rx_tmo = wait_for_completion_timeout(&rx_cmp, rx_tmo);
> + status = dma_async_is_tx_complete(rx_chan, rx_cookie,
> + NULL, NULL);
> +
> + if (rx_tmo == 0) {
> + pr_warn("%s: #%u: rx test timed out\n",
> + thread_name, total_tests - 1);
> + failed_tests++;
> + continue;
> + } else if (status != DMA_COMPLETE) {
> + pr_warn(
> + "%s: #%u: rx got completion callback, ",
> + thread_name, total_tests - 1);
> + pr_warn("but status is \'%s\'\n",
> + status == DMA_ERROR ? "error" :
> + "in progress");
> + failed_tests++;
> + continue;
> + }
> +
> + for (i = 0; i < frm_cnt; i++)
> + dma_unmap_single(rx_dev->dev, dma_dsts[i],
> + test_buf_size, DMA_DEV_TO_MEM);
> +
> + error_count = 0;
> +
> + pr_debug("%s: verifying source buffer...\n", thread_name);
> + error_count += xilinx_vdmatest_verify(thread->srcs, 0, 0,
> + 0, PATTERN_SRC, true);
> + error_count += xilinx_vdmatest_verify(thread->srcs, 0,
> + len, 0, PATTERN_SRC | PATTERN_COPY, true);
> + error_count += xilinx_vdmatest_verify(thread->srcs, len,
> + test_buf_size, len, PATTERN_SRC, true);
> +
> + pr_debug("%s: verifying dest buffer...\n",
> + thread->task->comm);
> + error_count += xilinx_vdmatest_verify(thread->dsts, 0, 0,
> + 0, PATTERN_DST, false);
> + error_count += xilinx_vdmatest_verify(thread->dsts, 0,
> + len, 0, PATTERN_SRC | PATTERN_COPY, false);
> + error_count += xilinx_vdmatest_verify(thread->dsts, len,
> + test_buf_size, len, PATTERN_DST, false);
> +
> + if (error_count) {
> + pr_warn("%s: #%u: %u errors with len=0x%x\n",
> + thread_name, total_tests - 1, error_count, len);
> + failed_tests++;
> + } else {
> + pr_debug("%s: #%u: No errors with len=0x%x\n",
> + thread_name, total_tests - 1, len);
> + }
> + }
> +
> + ret = 0;
> + for (i = 0; thread->dsts[i]; i++)
> + kfree(thread->dsts[i]);
> +err_dstbuf:
> + kfree(thread->dsts);
> +err_dsts:
> + for (i = 0; thread->srcs[i]; i++)
> + kfree(thread->srcs[i]);
> +err_srcbuf:
> + kfree(thread->srcs);
> +err_srcs:
> + pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
> + thread_name, total_tests, failed_tests, ret);
> +
> + if (iterations > 0)
> + while (!kthread_should_stop()) {
> + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_vdmatest_exit);
> + interruptible_sleep_on(&wait_vdmatest_exit);
> + }
> +
> + return ret;
> +}
> +
> +static void xilinx_vdmatest_cleanup_channel(struct xilinx_vdmatest_chan *dtc)
> +{
> + struct xilinx_vdmatest_slave_thread *thread, *_thread;
> + int ret;
> +
> + list_for_each_entry_safe(thread, _thread,
> + &dtc->threads, node) {
> + ret = kthread_stop(thread->task);
> + pr_info("xilinx_vdmatest: thread %s exited with status %d\n",
> + thread->task->comm, ret);
> + list_del(&thread->node);
> + kfree(thread);
> + }
> + kfree(dtc);
> +}
> +
> +static int
> +xilinx_vdmatest_add_slave_threads(struct xilinx_vdmatest_chan *tx_dtc,
> + struct xilinx_vdmatest_chan *rx_dtc)
> +{
> + struct xilinx_vdmatest_slave_thread *thread;
> + struct dma_chan *tx_chan = tx_dtc->chan;
> + struct dma_chan *rx_chan = rx_dtc->chan;
> +
> + thread = kzalloc(sizeof(struct xilinx_vdmatest_slave_thread),
> + GFP_KERNEL);
> + if (!thread)
> + pr_warn("xilinx_vdmatest: No memory for slave thread %s-%s\n",
> + dma_chan_name(tx_chan), dma_chan_name(rx_chan));
> +
> + thread->tx_chan = tx_chan;
> + thread->rx_chan = rx_chan;
> + thread->type = (enum dma_transaction_type)DMA_SLAVE;
> +
> + /* This barrier ensures the DMA channels in the 'thread'
> + * are initialized
> + */
> + smp_wmb();
> + thread->task = kthread_run(xilinx_vdmatest_slave_func, thread, "%s-%s",
> + dma_chan_name(tx_chan), dma_chan_name(rx_chan));
> + if (IS_ERR(thread->task)) {
> + pr_warn("xilinx_vdmatest: Failed to run thread %s-%s\n",
> + dma_chan_name(tx_chan), dma_chan_name(rx_chan));
> + kfree(thread);
> + }
> +
> + list_add_tail(&thread->node, &tx_dtc->threads);
> +
> + /* Added one thread with 2 channels */
> + return 1;
> +}
> +
> +static int xilinx_vdmatest_add_slave_channels(struct dma_chan *tx_chan,
> + struct dma_chan *rx_chan)
> +{
> + struct xilinx_vdmatest_chan *tx_dtc, *rx_dtc;
> + unsigned int thread_count = 0;
> +
> + tx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
> + if (!tx_dtc)
> + return -ENOMEM;
> +
> + rx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
> + if (!rx_dtc)
> + return -ENOMEM;
> +
> + tx_dtc->chan = tx_chan;
> + rx_dtc->chan = rx_chan;
> + INIT_LIST_HEAD(&tx_dtc->threads);
> + INIT_LIST_HEAD(&rx_dtc->threads);
> +
> + xilinx_vdmatest_add_slave_threads(tx_dtc, rx_dtc);
> + thread_count += 1;
> +
> + pr_info("xilinx_vdmatest: Started %u threads using %s %s\n",
> + thread_count, dma_chan_name(tx_chan), dma_chan_name(rx_chan));
> +
> + list_add_tail(&tx_dtc->node, &xilinx_vdmatest_channels);
> + list_add_tail(&rx_dtc->node, &xilinx_vdmatest_channels);
> + nr_channels += 2;
> +
> + return 0;
> +}
> +
> +static int xilinx_vdmatest_probe(struct platform_device *pdev)
> +{
> + struct dma_chan *chan, *rx_chan;
> + int err;
> +
> + err = of_property_read_u32(pdev->dev.of_node,
> + "xlnx,num-fstores", &frm_cnt);
> + if (err < 0) {
> + pr_err("xilinx_vdmatest: missing xlnx,num-fstores property\n");
> + return err;
> + }
> +
> + chan = dma_request_slave_channel(&pdev->dev, "vdma0");
> + if (IS_ERR(chan)) {
> + pr_err("xilinx_vdmatest: No Tx channel\n");
> + return PTR_ERR(chan);
> + }
> +
> + rx_chan = dma_request_slave_channel(&pdev->dev, "vdma1");
> + if (IS_ERR(rx_chan)) {
> + err = PTR_ERR(rx_chan);
> + pr_err("xilinx_vdmatest: No Rx channel\n");
> + goto free_tx;
> + }
> +
> + err = xilinx_vdmatest_add_slave_channels(chan, rx_chan);
> + if (err) {
> + pr_err("xilinx_vdmatest: Unable to add channels\n");
> + goto free_rx;
> + }
> + return 0;
> +
> +free_rx:
> + dma_release_channel(rx_chan);
> +free_tx:
> + dma_release_channel(chan);
> +
> + return err;
> +}
> +
> +static int xilinx_vdmatest_remove(struct platform_device *pdev)
> +{
> + struct xilinx_vdmatest_chan *dtc, *_dtc;
> + struct dma_chan *chan;
> +
> + list_for_each_entry_safe(dtc, _dtc, &xilinx_vdmatest_channels, node) {
> + list_del(&dtc->node);
> + chan = dtc->chan;
> + xilinx_vdmatest_cleanup_channel(dtc);
> + pr_info("xilinx_vdmatest: dropped channel %s\n",
> + dma_chan_name(chan));
> + dma_release_channel(chan);
> + }
> + return 0;
> +}
> +
> +static const struct of_device_id xilinx_vdmatest_of_ids[] = {
> + { .compatible = "xlnx,axi-vdma-test-1.00.a",},
> + {}
> +};
> +
> +static struct platform_driver xilinx_vdmatest_driver = {
> + .driver = {
> + .name = "xilinx_vdmatest",
> + .owner = THIS_MODULE,
> + .of_match_table = xilinx_vdmatest_of_ids,
> + },
> + .probe = xilinx_vdmatest_probe,
> + .remove = xilinx_vdmatest_remove,
> +};
> +
> +module_platform_driver(xilinx_vdmatest_driver);
> +
> +MODULE_AUTHOR("Xilinx, Inc.");
> +MODULE_DESCRIPTION("Xilinx AXI VDMA Test Client");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* Multiple CCR definitions
From: Balaji T K @ 2014-01-17 14:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGo_u6or57HEqRQjSjUXFpRNiFNbJyRYrsPX19Gf97J-sg4iHQ@mail.gmail.com>
On Friday 17 January 2014 12:09 AM, Nishanth Menon wrote:
> +linux-omap as well.
>
> On Thu, Jan 16, 2014 at 7:07 AM, Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
>> On Tue, Sep 10, 2013 at 11:05 PM, Linux Kernel Mailing List
>> <linux-kernel@vger.kernel.org> wrote:
>>> mmc: omap_hsmmc: use the generic config for omap2plus devices
>>>
>>> OMAP's hs_mmc driver is used for MMC controller operation on many
>>> omap2plus SoCs (OMAP2430, OMAP3, 4, 5 and AM335x).
>>>
>>> Considering that the device tree entries are already present for these,
>>> allow the driver to be built using the config ARCH_OMAP2PLUS rather
>>> than individually adding a config for each SoC to enable the
>>> support.
>>>
>>> Use COMPILE_TEST to enable the build for other platforms.
>>>
>>> Signed-off-by: Amarinder Bindra <a-bindra@ti.com>
>>> Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
>>> Cc: Nishanth Menon <nm@ti.com>
>>> Acked-by: Felipe Balbi <balbi@ti.com>
>>> Acked-by: Balaji T K <balajitk@ti.com>
>>> Signed-off-by: Chris Ball <cjb@laptop.org>
>>> ---
>>> drivers/mmc/host/Kconfig | 6 +++---
>>> 1 files changed, 3 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>>> index 84102bc..b8ce939 100644
>>> --- a/drivers/mmc/host/Kconfig
>>> +++ b/drivers/mmc/host/Kconfig
>>> @@ -284,11 +284,11 @@ config MMC_OMAP
>>>
>>> config MMC_OMAP_HS
>>> tristate "TI OMAP High Speed Multimedia Card Interface support"
>>> - depends on SOC_OMAP2430 || ARCH_OMAP3 || ARCH_OMAP4
>>> + depends on ARCH_OMAP2PLUS || COMPILE_TEST
>>
>> sh-allmodconfig (http://kisskb.ellerman.id.au/kisskb/buildresult/10477672/):
>> include/linux/omap-dma.h:171:8: error: expected identifier before
>> numeric constant
>> make[4]: *** [drivers/mmc/host/omap_hsmmc.o] Error 1
>>
>> It fails because sh #defines "CCR", which is one of the enum values in
>> include/linux/omap-dma.h.
>>
>> I expect a similar failure on blackfin, which also has #define CCR.
>>
>> Possible solutions:
>> 1. Rename CCR in sh/bfin to e.g. SH_CCR resp. BF_CCR.
>> 2. Use "depends on ARCH_OMAP2PLUS || (COMPILE_TEST && ARM)
omap-dma.h will be needed until all OMAPs are converted to DT only boot.
So depends on ARCH_OMAP2PLUS || (COMPILE_TEST && ARM) looks good to me
>> 3. Stop using include/linux/omap-dma.h, as it says:
>>
>> * Legacy OMAP DMA handling defines and functions
>> *
>> * NOTE: Do not use these any longer.
>> *
>> * Use the generic dmaengine functions as defined in
>> * include/linux/dmaengine.h.
>>
>> Gr{oetje,eeting}s,
>>
>> Geert
>>
>> --
>> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
>>
>> In personal conversations with technical people, I call myself a hacker. But
>> when I'm talking to journalists I just say "programmer" or something like that.
>> -- Linus Torvalds
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* [PATCH 12/20] ARM64 / ACPI: Use Parked Address in GIC structure for spin table SMP initialisation
From: Tomasz Nowicki @ 2014-01-17 14:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201401171515.21250.arnd@arndb.de>
On 01/17/14 15:15, Arnd Bergmann wrote:
> On Friday 17 January 2014, Hanjun Guo wrote:
>> +/* Parked Address in ACPI GIC structure */
>> +static u64 parked_address[NR_CPUS];
>
> Please use the per_cpu infrastructure rather than NR_CPUS long arrays.
Actually, per_cpu areas are initialized a bit later. If we really want
to use here per_cpu, then we need to implement early_per_cpu
Frankenstein like x86 does.
Tomasz
^ permalink raw reply
* [PATCH 07/20] ARM64 / ACPI: Enable ARM64 in Kconfig
From: Sudeep Holla @ 2014-01-17 14:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-8-git-send-email-hanjun.guo@linaro.org>
On 17/01/14 12:25, Hanjun Guo wrote:
> Add Kconfigs to build ACPI on ARM64, and make ACPI runable on ARM64.
>
> acpi_idle driver is x86/IA64 dependent now, so make CONFIG_ACPI_PROCESSOR
> depends on X86 || IA64, and implement it on ARM/ARM64 in the furture.
>
> In order to make arm-core.c can both run on ARM and ARM64, introduce
> CONFIG_ACPI_ARM to support it.
>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Al Stone <al.stone@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/Kconfig | 2 ++
> drivers/acpi/Kconfig | 11 ++++++++---
> drivers/acpi/plat/Makefile | 2 +-
> 3 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 6d4dd22..2b1fb1d 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -279,6 +279,8 @@ source "net/Kconfig"
>
> source "drivers/Kconfig"
>
> +source "drivers/acpi/Kconfig"
> +
> source "fs/Kconfig"
>
> source "arch/arm64/kvm/Kconfig"
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 4770de5..cae5dc9 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -2,13 +2,16 @@
> # ACPI Configuration
> #
>
> +config ACPI_ARM
> + bool
> +
> menuconfig ACPI
> bool "ACPI (Advanced Configuration and Power Interface) Support"
> depends on !IA64_HP_SIM
> - depends on IA64 || X86
> - depends on PCI
> + depends on ((IA64 || X86) && PCI) || ARM64
> select PNP
> - default y
> + select ACPI_ARM if ARM64
> + default y if !ARM64
> help
> Advanced Configuration and Power Interface (ACPI) support for
> Linux requires an ACPI-compliant platform (hardware/firmware),
> @@ -149,6 +152,7 @@ config ACPI_PROCESSOR
> tristate "Processor"
> select THERMAL
> select CPU_IDLE
> + depends on X86 || IA64
This doesn't look correct to me. You are disabling Perf/Idle/Thermal/Throttling
support for ARM64. Any particular reason ?
Also this is conflicting with other change where you are adding/exporting
boot_option_idle_override which is mainly used in processor_idle and that's
disabled for ARM64 here.
Regards,
Sudeep
^ permalink raw reply
* [PATCH] of: add function to count number of u32 elements in a property
From: Rob Herring @ 2014-01-17 14:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3781679.27UF2dWtKL@phil>
On Thu, Jan 16, 2014 at 12:04 PM, Heiko St?bner <heiko@sntech.de> wrote:
> The need to know the number of array elements in a property is
> a common pattern. To prevent duplication of open-coded implementations
> add a helper function that also centralises strict sanity checking
> and DTB format details.
>
> Suggested-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
> Hi Mark,
> did you mean it like this? I've tested it with the sram-reserve change and
> it made the part of the determining the number elements a lot nicer :-)
>
> drivers/of/base.c | 32 ++++++++++++++++++++++++++++++++
> include/linux/of.h | 8 ++++++++
> 2 files changed, 40 insertions(+)
>
> diff --git a/drivers/of/base.c b/drivers/of/base.c
> index f807d0e..0f40ea5 100644
> --- a/drivers/of/base.c
> +++ b/drivers/of/base.c
> @@ -920,6 +920,38 @@ int of_property_read_u32_index(const struct device_node *np,
> EXPORT_SYMBOL_GPL(of_property_read_u32_index);
>
> /**
> + * of_property_count_u32_elems - Count the number of u32 values in a property
> + *
> + * @np: device node from which the property value is to be read.
> + * @propname: name of the property to be searched.
> + *
> + * Search for a property in a device node and count the number of u32 elements
> + * in it. Returns number of elements on sucess, -EINVAL if the property does
> + * not exist or its length does not match a multiple of u32 and -ENODATA if the
> + * property does not have a value.
> + */
> +int of_property_count_u32_elems(const struct device_node *np,
> + const char *propname)
> +{
> + int elem_size = sizeof(u32);
This should be a parameter, so functions for different sized
properties can be a simple wrapper function.
> + int len;
> + struct property *prop = of_find_property(np, propname, &len);
> +
> + if (!prop)
> + return -EINVAL;
> + if (!prop->value)
> + return -ENODATA;
> +
> + if (prop->length % elem_size != 0) {
> + pr_err("size of %s is not a multiple of u32\n", propname);
The node name would be useful here too.
> + return -EINVAL;
> + }
> +
> + return len / elem_size;
> +}
> +EXPORT_SYMBOL_GPL(of_property_count_u32_elems);
> +
> +/**
> * of_property_read_u8_array - Find and read an array of u8 from a property.
> *
> * @np: device node from which the property value is to be read.
> diff --git a/include/linux/of.h b/include/linux/of.h
> index 276c546..5794942 100644
> --- a/include/linux/of.h
> +++ b/include/linux/of.h
> @@ -253,6 +253,8 @@ extern struct property *of_find_property(const struct device_node *np,
> extern int of_property_read_u32_index(const struct device_node *np,
> const char *propname,
> u32 index, u32 *out_value);
> +extern int of_property_count_u32_elems(const struct device_node *np,
> + const char *propname);
> extern int of_property_read_u8_array(const struct device_node *np,
> const char *propname, u8 *out_values, size_t sz);
> extern int of_property_read_u16_array(const struct device_node *np,
> @@ -432,6 +434,12 @@ static inline int of_property_read_u32_index(const struct device_node *np,
> return -ENOSYS;
> }
>
> +static inline int of_property_count_u32_elems(const struct device_node *np,
> + const char *propname)
> +{
> + return -ENOSYS;
> +}
> +
> static inline int of_property_read_u8_array(const struct device_node *np,
> const char *propname, u8 *out_values, size_t sz)
> {
> --
> 1.7.10.4
>
>
^ permalink raw reply
* [PATCH] mmc: dw_mmc: fix dw_mci_get_cd
From: Chris Ball @ 2014-01-17 14:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <003401cf1380$a194aa10$e4bdfe30$%jun@samsung.com>
Hi,
On Fri, Jan 17 2014, Seungwon Jeon wrote:
> On Fri, January 17, 2014, Kevin Hilman wrote:
>> Zhangfei Gao <zhangfei.gao@linaro.org> writes:
>>
>> > Introduced from commit bf626e5550f24aec24975a0e85ad8e572ca76a6b
>> > CDETECT is ignored since negated return value of mmc_gpio_get_cd(mmc)
>> > can not be checked by IS_ERR_VALUE.
>> > Add spin_lock_bh(&host->lock) for atomic accessing DW_MMC_CARD_PRESENT,
>> > otherwise sd detect may occasionally fail.
>> >
>> > Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> > Reported-by: Kevin Hilman <khilman@linaro.org>
>> > Reviewed-by: Sachin Kamat <sachin.kamat@linaro.org>
>> > Tested-by: Sachin Kamat <sachin.kamat@linaro.org>
>>
>> I didn't look at the patch in detail, but can at least confirm that
>> Arndale boots again in -next with this patch applied.
>>
>> Tested-by: Kevin Hilman <khilman@linaro.org>
>
> Acked-by: Seungwon Jeon <tgih.jun@samsung.com>
Thanks, pushed to mmc-next for 3.14.
- Chris.
--
Chris Ball <chris@printf.net> <http://printf.net/>
^ permalink raw reply
* [PATCH 03/20] ARM64 / ACPI: Introduce the skeleton of _PDC related for ARM64
From: Sudeep Holla @ 2014-01-17 14:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-4-git-send-email-hanjun.guo@linaro.org>
On 17/01/14 12:24, Hanjun Guo wrote:
> The _PDC (Processor Driver Capabilities) object provides OSPM a
> mechanism to convey to the platform the capabilities supported
> by OSPM for processor power management.
>
> OSPM evaluates _PDC prior to evaluating any other processor
> power management objects returning configuration information.
>
> This patch introduces the skeleton of _PDC related file to make
> ACPI core can be compiled on ARM64.
>
> Signed-off-by: Al Stone <al.stone@linaro.org>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/include/asm/acpi.h | 32 ++++++++++++++++++++++++++++++++
> arch/arm64/include/asm/processor.h | 2 ++
> arch/arm64/kernel/process.c | 3 +++
> 3 files changed, 37 insertions(+)
> create mode 100644 arch/arm64/include/asm/acpi.h
>
> diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
> new file mode 100644
> index 0000000..cf19dc6
> --- /dev/null
> +++ b/arch/arm64/include/asm/acpi.h
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright (C) 2013, Al Stone <al.stone@linaro.org>
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * 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
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + */
> +
> +#ifndef _ASM_ARM64_ACPI_H
> +#define _ASM_ARM64_ACPI_H
> +
> +static inline bool arch_has_acpi_pdc(void)
> +{
> + return false; /* always false for now */
> +}
> +
> +static inline void arch_acpi_set_pdc_bits(u32 *buf)
> +{
> + return;
> +}
> +
> +#endif /*_ASM_ARM64_ACPI_H*/
> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> index 45b20cd..50ce951 100644
> --- a/arch/arm64/include/asm/processor.h
> +++ b/arch/arm64/include/asm/processor.h
> @@ -162,6 +162,8 @@ static inline void spin_lock_prefetch(const void *x)
>
> #define HAVE_ARCH_PICK_MMAP_LAYOUT
>
> +extern unsigned long boot_option_idle_override;
> +
> #endif
>
> #endif /* __ASM_PROCESSOR_H */
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index de17c89..13d3d7f 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -89,6 +89,9 @@ void arch_cpu_idle_prepare(void)
> local_fiq_enable();
> }
>
> +unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
> +EXPORT_SYMBOL(boot_option_idle_override);
> +
This is what I mentioned in other email. Do we really foresee use of this in
ARM64 or it's just added to avoid build issues ?
Regards,
Sudeep
^ permalink raw reply
* [PATCH RFC 00/26] Migrate more OMAP DMA code to DMA engine
From: Sricharan R @ 2014-01-17 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140113141713.GC27282@n2100.arm.linux.org.uk>
On Monday 13 January 2014 07:47 PM, Russell King - ARM Linux wrote:
> On Fri, Jan 10, 2014 at 05:46:20PM +0530, Sricharan R wrote:
>> I tested this series on DRA7 with mmc peripheral dma and it looked
>> fine. Some how dmaengine test cases were not fine. But that may not
>> have anything to do with this series. I will check more on that and
>> will come back with that results as well.
>
> Not fine in what sense?
>
Sorry for delayed response.
So in the case of dmaengine test case, it looks for channels
with DMA_MEMCPY, DMA_XOR, DMA_PQ capabilities. But the omap-dma
device is not registered with MEMCPY capabilities, hence the
testcase fails as no channels are available.
Regards,
Sricharan
^ permalink raw reply
* [PATCH 02/20] ARM64 : Add dummy asm/cpu.h
From: Sudeep Holla @ 2014-01-17 14:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-3-git-send-email-hanjun.guo@linaro.org>
On 17/01/14 12:24, Hanjun Guo wrote:
> ACPI requires a cpu.h, add a dummy one copied from arm. This will need
> updated or replaced as ACPI based cpu hotplug or cpu topology for armv8
> is worked out.
>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/include/asm/cpu.h | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
> create mode 100644 arch/arm64/include/asm/cpu.h
>
> diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
> new file mode 100644
> index 0000000..8625eb1
> --- /dev/null
> +++ b/arch/arm64/include/asm/cpu.h
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright (C) 2004-2005 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#ifndef __ASM_ARM_CPU_H
> +#define __ASM_ARM_CPU_H
> +
> +#include <linux/percpu.h>
> +#include <linux/cpu.h>
> +#include <linux/topology.h>
> +
> +struct cpuinfo_arm {
> + struct cpu cpu;
> + u64 cpuid;
> +#ifdef CONFIG_SMP
> + unsigned int loops_per_jiffy;
IIUC we don't have loop-based delay support in ARM64.
It's always timer based and in which case we don't need loops_per_jiffy
Regards,
Sudeep
^ permalink raw reply
* [PATCH 18/20] clocksource / acpi: Add macro CLOCKSOURCE_ACPI_DECLARE
From: Arnd Bergmann @ 2014-01-17 14:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-19-git-send-email-hanjun.guo@linaro.org>
On Friday 17 January 2014, Hanjun Guo wrote:
>
> From: Amit Daniel Kachhap <amit.daniel@samsung.com>
>
> This macro does the same job as CLOCKSOURCE_OF_DECLARE. The device
> name from the ACPI timer table is matched with all the registered
> timer controllers and matching initialisation routine is invoked.
I wouldn't anticipate this infrastructure to be required. Shouldn't all
ARMv8 machines have an architected timer?
Arnd
^ permalink raw reply
* [PATCH 12/20] ARM64 / ACPI: Use Parked Address in GIC structure for spin table SMP initialisation
From: Arnd Bergmann @ 2014-01-17 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-13-git-send-email-hanjun.guo@linaro.org>
On Friday 17 January 2014, Hanjun Guo wrote:
> +/* Parked Address in ACPI GIC structure */
> +static u64 parked_address[NR_CPUS];
Please use the per_cpu infrastructure rather than NR_CPUS long arrays.
Arnd
^ permalink raw reply
* [PATCH 09/20] ARM64 / ACPI: Implement core functions for parsing MADT table
From: Arnd Bergmann @ 2014-01-17 14:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-10-git-send-email-hanjun.guo@linaro.org>
On Friday 17 January 2014, Hanjun Guo wrote:
>
> +/*
> + * Local interrupt controller address,
> + * GIC cpu interface base address on ARM/ARM64
> + */
> +static u64 acpi_lapic_addr __initdata;
If it's cpu local, don't you need more than one address to support SMP?
Also, the variable appears to be write-only.
> +#define BAD_MADT_ENTRY(entry, end) ( \
> + (!entry) || (unsigned long)entry + sizeof(*entry) > end || \
> + ((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
> +
Better make this an inline function.
> +static int __init
> +acpi_parse_gic(struct acpi_subtable_header *header, const unsigned long end)
> +{
> + struct acpi_madt_generic_interrupt *processor = NULL;
> +
> + processor = (struct acpi_madt_generic_interrupt *)header;
You don't need the initialization in the first line when you write to the
variable before reading it. Same in the other functions.
Arnd
^ permalink raw reply
* [PATCH 04/20] ARM64 / ACPI: Introduce arm_core.c and its related head file
From: Will Deacon @ 2014-01-17 14:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-5-git-send-email-hanjun.guo@linaro.org>
On Fri, Jan 17, 2014 at 12:24:58PM +0000, Hanjun Guo wrote:
> Introduce arm_core.c and its related head file, after this patch,
> we can get ACPI tables from firmware on ARM64 now.
>
> Signed-off-by: Al Stone <al.stone@linaro.org>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
[...]
> diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
> index bd9bbd0..2210353 100644
> --- a/arch/arm64/kernel/setup.c
> +++ b/arch/arm64/kernel/setup.c
> @@ -41,6 +41,7 @@
> #include <linux/memblock.h>
> #include <linux/of_fdt.h>
> #include <linux/of_platform.h>
> +#include <linux/acpi.h>
>
> #include <asm/cputype.h>
> #include <asm/elf.h>
> @@ -225,6 +226,11 @@ void __init setup_arch(char **cmdline_p)
>
> arm64_memblock_init();
>
> + /* Parse the ACPI tables for possible boot-time configuration */
> + acpi_boot_table_init();
> + early_acpi_boot_init();
> + acpi_boot_init();
Do we really need *three* back-to-back calls for ACPI to initialise?
Will
^ permalink raw reply
* [PATCH v4 net-next 2/4] sh_eth: Add support for r7s72100
From: Sergei Shtylyov @ 2014-01-17 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140117061301.GD16455@verge.net.au>
Hello.
On 17-01-2014 10:13, Simon Horman wrote:
>>>>>>> This is a fast ethernet controller.
>>>>>>> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
>>>>>> [...]
>>>>>>> diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
>>>>>>> index 4b38533..cc6d4af 100644
>>>>>>> --- a/drivers/net/ethernet/renesas/sh_eth.c
>>>>>>> +++ b/drivers/net/ethernet/renesas/sh_eth.c
>>>>>>> @@ -190,6 +190,59 @@ static const u16 sh_eth_offset_fast_rcar[SH_ETH_MAX_REGISTER_OFFSET] = {
>>>> [...]
>>>>>>> @@ -701,6 +762,35 @@ static struct sh_eth_cpu_data r8a7740_data = {
>>>>>>> .shift_rd0 = 1,
>>>>>>> };
>>>>>>>
>>>>>>> +/* R7S72100 */
>>>>>>> +static struct sh_eth_cpu_data r7s72100_data = {
>>>>>>> + .chip_reset = sh_eth_chip_reset,
>>>>>>> + .set_duplex = sh_eth_set_duplex,
>>>>>>> +
>>>>>>> + .register_type = SH_ETH_REG_FAST_RZ,
>>>>>>> +
>>>>>>> + .ecsr_value = ECSR_ICD,
>>>>>>> + .ecsipr_value = ECSIPR_ICDIP,
>>>>>>> + .eesipr_value = 0xff7f009f,
>>>>>>> +
>>>>>>> + .tx_check = EESR_TC1 | EESR_FTC,
>>>>>>> + .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
>>>>>>> + EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
>>>>>>> + EESR_TDE | EESR_ECI,
>>>>>>> + .fdr_value = 0x0000070f,
>>>>>>> + .rmcr_value = RMCR_RNC,
>>>>>>> +
>>>>>>> + .apr = 1,
>>>>>>> + .mpr = 1,
>>>>>>> + .tpauser = 1,
>>>>>>> + .hw_swap = 1,
>>>>>>> + .rpadir = 1,
>>>>>>> + .rpadir_value = 2 << 16,
>>>>>>> + .no_trimd = 1,
>>>>>>> + .tsu = 1,
>>>>>>> + .shift_rd0 = 1,
>>>>>> Perhaps this field should be renamed to something talking about
>>>>>> check summing support (since bits 0..15 of RD0 contain a frame check
>>>>>> sum for those SoCs). Or maybe it should be just merged with the
>>>>>> 'hw_crc' field...
>>>>> I have no feelings about that one way or another.
>>>> Do you happen to have R8A7740 manual by chance? If so, does it
>>>> talk about RX check summing support and using RD0 for that?
>>> Yes and yes.
>>> I have taken a quick look and the documentation for RX checksumming on the
>>> R8A7740 appears to be very similar if not the same as that of the R7S72100.
>>> In particular both refer to using the bottom 16 bits of RD0 as
>>> containing the packet checksum.
>> OK, now if you had SH7734 manual to completely confirm that check
>> sum is stored in the same place there... most probably it is, of
>> course, and we should merge 'hw_crc' and 'shift_rd0' into a single
>> field.
> Unfortunately I don't have access to that manual.
Anyway, we also need Gen2 manuals accepting the fact that checksumming is
also supported (they also set 'shift_rd0' field) and giving the mapping of CSMR...
WBR, Sergei
^ permalink raw reply
* [PATCH 06/20] ARM64 / ACPI: Introduce some PCI functions when PCI is enabled
From: Arnd Bergmann @ 2014-01-17 14:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-7-git-send-email-hanjun.guo@linaro.org>
On Friday 17 January 2014, Hanjun Guo wrote:
> +++ b/arch/arm64/pci/Makefile
> @@ -0,0 +1 @@
> +obj-y += pci.o
> diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c
> new file mode 100644
> index 0000000..4e46790
> --- /dev/null
> +++ b/arch/arm64/pci/pci.c
> @@ -0,0 +1,33 @@
> +#include <linux/acpi.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +
> +/**
> + * raw_pci_read - Platform-specific PCI config space access.
> + *
> + * Default empty implementation. Replace with an architecture-specific setup
> + * routine, if necessary.
> + */
> +int __weak raw_pci_read(unsigned int domain, unsigned int bus,
> + unsigned int devfn, int reg, int len, u32 *val)
> +{
> + return -EINVAL;
> +}
> +
> +int __weak raw_pci_write(unsigned int domain, unsigned int bus,
> + unsigned int devfn, int reg, int len, u32 val)
> +{
> + return -EINVAL;
> +}
I'd rather not see __weak functions here. Just provide them unconditionally
so that we can add a proper implementation when needed. You could also
define these as 'static inline' in a header file to keep them from consuming
space in the object code.
> diff --git a/drivers/acpi/plat/arm-core.c b/drivers/acpi/plat/arm-core.c
> index 3c8521d..1835b21 100644
> --- a/drivers/acpi/plat/arm-core.c
> +++ b/drivers/acpi/plat/arm-core.c
> @@ -100,6 +100,25 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
> }
> EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
>
> +int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi)
> +{
> + return -1;
> +}
> +
> +int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
> +{
> + /* TBD */
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL(acpi_register_ioapic);
> +
> +int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
> +{
> + /* TBD */
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL(acpi_unregister_ioapic);
> +
My feeling is that these are better handled in the ACPI code by not
calling them on architectures that have no ISA or no IOAPIC support.
We have configuration symbols for both, so you don't have to make
it depend on CONFIG_ARM64 or CONFIG_X86.
Arnd
^ permalink raw reply
* Deadlock in do_page_fault() on ARM (old kernel)
From: Russell King - ARM Linux @ 2014-01-17 13:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D73220.3030108@signal11.us>
On Wed, Jan 15, 2014 at 08:13:04PM -0500, Alan Ott wrote:
> So my questions are:
> 1. Why don't I see a full backtrace beyond the exception stack? It's the
> same when dump_stack() is called manually.
No idea - it looks like you're not using frame pointers, but are using
the unwinder. Full backtraces can always be created with frame pointers,
it's just that unwinding seems unreliable.
I think we do need to see the full backtrace here - from looking at the
full state dump, I don't see any sign of the mmap_sem being held except
by an attempt to process a fault, and two threads trying to do a
sys_mmap_pgoff().
My suspicion therefore is that some other thread must have died while
holding the mmap_sem, so there's probably a kernel oops earlier...
that's my best guess at the moment without seeing the full backtrace.
--
FTTC broadband for 0.8mile line: 5.8Mbps down 500kbps up. Estimation
in database were 13.1 to 19Mbit for a good line, about 7.5+ for a bad.
Estimate before purchase was "up to 13.2Mbit".
^ permalink raw reply
* debugging head.S
From: Darryl @ 2014-01-17 13:41 UTC (permalink / raw)
To: linux-arm-kernel
I am trying to build 3.12.6 for an OMAP3-based board (debug console on
UART 3)
Enabling DEBUG in
arch/arm/boot/compressed/head.S
and with
CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S"
compiling (well, actually, assembling) gives the following (slightly
reformatted):
arch/arm/boot/compressed/head.S: Assembler messages:
arch/arm/boot/compressed/head.S:1237: Error: ARM register expected
-- `sub ,r3,r1'
arch/arm/boot/compressed/head.S:1237: Error: ARM register expected
-- `ldr r3,[,#0]'
arch/arm/boot/compressed/head.S:1237: Error: ARM register expected
-- `ldr r1,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#0]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#0]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `ldr ,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: shift expression
expected -- `add r3,r3,'
arch/arm/include/debug/omap2plus.S:138: Error: shift expression
expected -- `add r1,r1,'
arch/arm/boot/compressed/head.S:1255: Error: ARM register expected
-- `sub ,r3,r1'
arch/arm/boot/compressed/head.S:1255: Error: ARM register expected
-- `ldr r3,[,#0]'
arch/arm/boot/compressed/head.S:1255: Error: ARM register expected
-- `ldr r1,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#0]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#0]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#4]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `str r3,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: ARM register
expected -- `ldr ,[,#8]'
arch/arm/include/debug/omap2plus.S:138: Error: shift expression
expected -- `add r3,r3,'
arch/arm/include/debug/omap2plus.S:138: Error: shift expression
expected -- `add r1,r1,'
It looks like the 'addruart' macro in in omap2plus.S is shorted a
parameter from the macro 'loadsp' macro in head.S, called in 'puts' and
'putc'.
Am I misusing this in some way? Any suggestions?
Darryl
^ permalink raw reply
* [PATCH] arch_timer: Move delay timer to drivers clocksource
From: Prashant Gaikwad @ 2014-01-17 13:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D91D3A.80800@linaro.org>
On Friday 17 January 2014 05:38 PM, Daniel Lezcano wrote:
> On 01/17/2014 12:37 PM, Prashant Gaikwad wrote:
>> On Friday 17 January 2014 03:45 PM, Daniel Lezcano wrote:
>>> On 01/17/2014 11:11 AM, Prashant Gaikwad wrote:
>>>> On Friday 17 January 2014 02:42 PM, Daniel Lezcano wrote:
>>>>> On 01/17/2014 10:07 AM, Antti Miettinen wrote:
>>>>>> Will Deacon <will.deacon@arm.com> writes:
>>>>>>> Why can't you use the C3STOP feature so that the arch-timer isn't
>>>>>>> used when
>>>>>>> you go idle?
>>>>>> That would mean falling back to broadcast timer, right? That's not
>>>>>> necessarily on the local CPU so wakeups would often wake two CPUs.
>>>>> You can prevent that if the hardware supports it with the
>>>>> CLOCK_EVT_DYNIRQ flag on the broadcast timer.
>>>> Instead of falling back on broadcast timer, is it possible to fall back
>>>> on other per-CPU timer which is preserved across idle state?
>>> Is it what you are looking for ?
>>>
>>> http://lwn.net/Articles/580568/
>>>
>> If I understand correctly these patches enables us to use per-CPU timers
>> as broadcast timers. I do not want to use broadcast timer.
> Why ?
>
For some idle states it may be required to change the timer when
entering idle state to adjust the exit latency.
It can be done for broadcast timer too but following scenario will not work
1. CPU1 enters in idle state:
Broadcast timer next event is in 2ms, CPU latency is 50us. So
we change the broadcast timer to send event after (2ms - 50us).
2. After 1ms CPU2 enters in idle state:
Next event is 5ms. Broadcast timer is already programmed to <
(5ms -50us) so we do nothing.
3. CPU1 exits from idle state because of timer interrupt
4. Broadcast event handler:
- Timer event is handled and CPU1 is switched back to local timer.
- Next CPU is CPU2 and next event for it is 4ms. So brodcast timer
is programmed to 4ms.
We can not change brodcast timer here to adjust delay caused by CPU exit
latency.
CPU idle governors does help to solve the latency issue. I was thinking
this from sub-states perspective which are not exposed to CPU idle governor.
Solution for this could be to expose those states to CPU idle governor
but just wanted to know if we can use timers this way?
Another requirement:
We have 3 timers T1, T2, T3 used as wake events for 3 idle states C1,
C2, C3 respectively.
Rating of T2 is better than T3. If I register T2 and T3 both as
broadcast timers then T3 will not be used. But ...
- T2 is not preserved in C3 idle state.
- T3 resolution is very poor (ms) and can not be used as wake event
for C2.
Possible solution, register only T3 as broadcast device and use T2 as
per-CPU fallback timer.
>> If I have 2 per-CPU timers T1 and T2, T1 is not preserved across idle
>> state and T2 is preserved. And I want to use T1 as scheduler timer.
>> Can I do following for idle state?
>>
>> Idle entry:
>> clockevents_shutdown(T1);
>> clockevents_set_mode(T2, ONESHOT);
>> clockevents_program_event(T2, next_event);
>>
>> Idle exit:
>> clockevents_shutdown(T2);
>> clockevents_set_mode(T1, ONESHOT);
>
>
>
>>>>>> Does
>>>>>> anyone have patches for using a CPU local timer as a fallback for
>>>>>> C3STOP timers?
>
^ permalink raw reply
* [PATCH net-next v3 8/8] net: stmmac: sunxi platform extensions for GMAC in Allwinner A20 SoC's
From: Chen-Yu Tsai @ 2014-01-17 13:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389965087-21971-1-git-send-email-wens@csie.org>
The Allwinner A20 has an ethernet controller that seems to be
an early version of Synopsys DesignWare MAC 10/100/1000 Universal,
which is supported by the stmmac driver.
Allwinner's GMAC requires setting additional registers in the SoC's
clock control unit.
The exact version of the DWMAC IP that Allwinner uses is unknown,
thus the exact feature set is unknown.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../bindings/net/allwinner,sun7i-a20-gmac.txt | 27 ++++
drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 ++
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c | 141 +++++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 3 +
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 +
6 files changed, 186 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.txt
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.txt b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.txt
new file mode 100644
index 0000000..ea4d752
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.txt
@@ -0,0 +1,27 @@
+* Allwinner GMAC ethernet controller
+
+This device is a platform glue layer for stmmac.
+Please see stmmac.txt for the other unchanged properties.
+
+Required properties:
+ - compatible: Should be "allwinner,sun7i-a20-gmac"
+ - clocks: Should contain the GMAC main clock, and tx clock
+ The tx clock type should be "allwinner,sun7i-a20-gmac-clk"
+ - clock-names: Should contain the clock names "stmmaceth",
+ and "allwinner_gmac_tx"
+
+Optional properties:
+- phy-supply: phandle to a regulator if the PHY needs one
+
+Examples:
+
+ gmac: ethernet at 01c50000 {
+ compatible = "allwinner,sun7i-a20-gmac";
+ reg = <0x01c50000 0x10000>,
+ <0x01c20164 0x4>;
+ interrupts = <0 85 1>;
+ interrupt-names = "macirq";
+ clocks = <&ahb_gates 49>, <&gmac_tx>;
+ clock-names = "stmmaceth", "allwinner_gmac_tx";
+ phy-mode = "mii";
+ };
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index b59d1ef..e2f202e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -26,6 +26,17 @@ config STMMAC_PLATFORM
If unsure, say N.
+config DWMAC_SUNXI
+ bool "Allwinner GMAC support"
+ depends on STMMAC_PLATFORM && ARCH_SUNXI
+ default y
+ ---help---
+ Support for Allwinner A20/A31 GMAC ethernet controllers.
+
+ This selects Allwinner SoC glue layer support for the
+ stmmac device driver. This driver is used for A20/A31
+ GMAC ethernet controller.
+
config STMMAC_PCI
bool "STMMAC PCI bus support"
depends on STMMAC_ETH && PCI
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 356a9dd..ecadece 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
+stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
new file mode 100644
index 0000000..dc4f8ae
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
@@ -0,0 +1,141 @@
+/**
+ * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
+ *
+ * Copyright (C) 2013 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/stmmac.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/regulator/consumer.h>
+
+struct sunxi_priv_data {
+ int interface;
+ int clk_enabled;
+ struct clk *tx_clk;
+ struct regulator *regulator;
+};
+
+static void *sun7i_gmac_setup(struct platform_device *pdev)
+{
+ struct sunxi_priv_data *gmac;
+ struct device *dev = &pdev->dev;
+
+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
+ if (!gmac)
+ return ERR_PTR(-ENOMEM);
+
+ gmac->interface = of_get_phy_mode(dev->of_node);
+
+ gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
+ if (IS_ERR(gmac->tx_clk)) {
+ dev_err(dev, "could not get tx clock\n");
+ return gmac->tx_clk;
+ }
+
+ /* Optional regulator for PHY */
+ gmac->regulator = devm_regulator_get_optional(dev, "phy");
+ if (IS_ERR(gmac->regulator)) {
+ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
+ return ERR_PTR(-EPROBE_DEFER);
+ dev_info(dev, "no regulator found\n");
+ gmac->regulator = NULL;
+ }
+
+ return gmac;
+}
+
+#define SUN7I_GMAC_GMII_RGMII_RATE 125000000
+#define SUN7I_GMAC_MII_RATE 25000000
+
+static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
+{
+ struct sunxi_priv_data *gmac = priv;
+ int ret;
+
+ if (gmac->regulator) {
+ ret = regulator_enable(gmac->regulator);
+ if (ret)
+ return ret;
+ }
+
+ /* Set GMAC interface port mode
+ *
+ * The GMAC TX clock lines are configured by setting the clock
+ * rate, which then uses the auto-reparenting feature of the
+ * clock driver, and enabling/disabling the clock.
+ */
+ if (gmac->interface == PHY_INTERFACE_MODE_RGMII) {
+ clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
+ clk_prepare_enable(gmac->tx_clk);
+ gmac->clk_enabled = 1;
+ } else {
+ clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
+ clk_prepare(gmac->tx_clk);
+ }
+
+ return 0;
+}
+
+static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
+{
+ struct sunxi_priv_data *gmac = priv;
+
+ if (gmac->clk_enabled) {
+ clk_disable(gmac->tx_clk);
+ gmac->clk_enabled = 0;
+ }
+ clk_unprepare(gmac->tx_clk);
+
+ if (gmac->regulator)
+ regulator_disable(gmac->regulator);
+}
+
+static void sun7i_fix_speed(void *priv, unsigned int speed)
+{
+ struct sunxi_priv_data *gmac = priv;
+
+ /* only GMII mode requires us to reconfigure the clock lines */
+ if (gmac->interface != PHY_INTERFACE_MODE_GMII)
+ return;
+
+ if (gmac->clk_enabled) {
+ clk_disable(gmac->tx_clk);
+ gmac->clk_enabled = 0;
+ }
+ clk_unprepare(gmac->tx_clk);
+
+ if (speed == 1000) {
+ clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
+ clk_prepare_enable(gmac->tx_clk);
+ gmac->clk_enabled = 1;
+ } else {
+ clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
+ clk_prepare(gmac->tx_clk);
+ }
+}
+
+/* of_data specifying hardware features and callbacks.
+ * hardware features were copied from Allwinner drivers. */
+const struct stmmac_of_data sun7i_gmac_data = {
+ .has_gmac = 1,
+ .tx_coe = 1,
+ .fix_mac_speed = sun7i_fix_speed,
+ .setup = sun7i_gmac_setup,
+ .init = sun7i_gmac_init,
+ .exit = sun7i_gmac_exit,
+};
+
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index c1c141f..d9af26e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -130,6 +130,9 @@ void stmmac_disable_eee_mode(struct stmmac_priv *priv);
bool stmmac_eee_init(struct stmmac_priv *priv);
#ifdef CONFIG_STMMAC_PLATFORM
+#ifdef CONFIG_DWMAC_SUNXI
+extern const struct stmmac_of_data sun7i_gmac_data;
+#endif
extern struct platform_driver stmmac_pltfr_driver;
static inline int stmmac_register_platform(void)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index bf119db..9d4baa8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -30,6 +30,9 @@
#include "stmmac.h"
static const struct of_device_id stmmac_dt_ids[] = {
+#ifdef CONFIG_DWMAC_SUNXI
+ { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
+#endif
/* SoC specific glue layers should come before generic bindings */
{ .compatible = "st,spear600-gmac"},
{ .compatible = "snps,dwmac-3.610"},
--
1.8.5.2
^ permalink raw reply related
* [PATCH net-next v3 7/8] net: stmmac: Use driver data and callbacks tied with compatible strings
From: Chen-Yu Tsai @ 2014-01-17 13:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389965087-21971-1-git-send-email-wens@csie.org>
The stmmac driver core allows passing feature flags and callbacks via
platform data. Add a similar stmmac_of_data to pass flags and callbacks
tied to compatible strings. This allows us to extend stmmac with glue
layers for different SoCs.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 44 +++++++++++++++++-----
include/linux/stmmac.h | 18 +++++++++
2 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 82110f1..bf119db 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -26,8 +26,20 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
+#include <linux/of_device.h>
#include "stmmac.h"
+static const struct of_device_id stmmac_dt_ids[] = {
+ /* SoC specific glue layers should come before generic bindings */
+ { .compatible = "st,spear600-gmac"},
+ { .compatible = "snps,dwmac-3.610"},
+ { .compatible = "snps,dwmac-3.70a"},
+ { .compatible = "snps,dwmac-3.710"},
+ { .compatible = "snps,dwmac"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
+
#ifdef CONFIG_OF
static int stmmac_probe_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
@@ -35,10 +47,32 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
{
struct device_node *np = pdev->dev.of_node;
struct stmmac_dma_cfg *dma_cfg;
+ const struct of_device_id *device;
if (!np)
return -ENODEV;
+ device = of_match_device(stmmac_dt_ids, &pdev->dev);
+ if (!device)
+ return -ENODEV;
+
+ if (device->data) {
+ const struct stmmac_of_data *data = device->data;
+ plat->has_gmac = data->has_gmac;
+ plat->enh_desc = data->enh_desc;
+ plat->tx_coe = data->tx_coe;
+ plat->rx_coe = data->rx_coe;
+ plat->bugged_jumbo = data->bugged_jumbo;
+ plat->pmt = data->pmt;
+ plat->riwt_off = data->riwt_off;
+ plat->fix_mac_speed = data->fix_mac_speed;
+ plat->bus_setup = data->bus_setup;
+ plat->setup = data->setup;
+ plat->free = data->free;
+ plat->init = data->init;
+ plat->exit = data->exit;
+ }
+
*mac = of_get_mac_address(np);
plat->interface = of_get_phy_mode(np);
@@ -259,16 +293,6 @@ static int stmmac_pltfr_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops,
stmmac_pltfr_suspend, stmmac_pltfr_resume);
-static const struct of_device_id stmmac_dt_ids[] = {
- { .compatible = "st,spear600-gmac"},
- { .compatible = "snps,dwmac-3.610"},
- { .compatible = "snps,dwmac-3.70a"},
- { .compatible = "snps,dwmac-3.710"},
- { .compatible = "snps,dwmac"},
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
-
struct platform_driver stmmac_pltfr_driver = {
.probe = stmmac_pltfr_probe,
.remove = stmmac_pltfr_remove,
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 0a5a7ac..1367974 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -121,4 +121,22 @@ struct plat_stmmacenet_data {
void *custom_data;
void *bsp_priv;
};
+
+/* of_data for SoC glue layer device tree bindings */
+
+struct stmmac_of_data {
+ int has_gmac;
+ int enh_desc;
+ int tx_coe;
+ int rx_coe;
+ int bugged_jumbo;
+ int pmt;
+ int riwt_off;
+ void (*fix_mac_speed)(void *priv, unsigned int speed);
+ void (*bus_setup)(void __iomem *ioaddr);
+ void *(*setup)(struct platform_device *pdev);
+ void (*free)(struct platform_device *pdev, void *priv);
+ int (*init)(struct platform_device *pdev, void *priv);
+ void (*exit)(struct platform_device *pdev, void *priv);
+};
#endif
--
1.8.5.2
^ permalink raw reply related
* [PATCH net-next v3 6/8] net: stmmac: Deprecate snps, phy-addr and auto-detect PHY address
From: Chen-Yu Tsai @ 2014-01-17 13:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389965087-21971-1-git-send-email-wens@csie.org>
The snps,phy-addr device tree property is non-standard, and should be
removed in favor of proper phy node support. Remove it from the binding
documents and warn if the property is still used.
Most PHYs respond to address 0, but a few don't, so auto-detect PHY
address by default, to make up for the lack of explicit address selection.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
Documentation/devicetree/bindings/net/stmmac.txt | 1 -
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 9 ++++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index d132513..aefb639 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -12,7 +12,6 @@ Required properties:
property
- phy-mode: String, operation mode of the PHY interface.
Supported values are: "mii", "rmii", "gmii", "rgmii".
-- snps,phy-addr phy address to connect to.
- snps,reset-gpio gpio number for phy reset.
- snps,reset-active-low boolean flag to indicate if phy reset is active low.
- snps,reset-delays-us is triplet of delays
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 634260e..82110f1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -50,7 +50,14 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
if (plat->bus_id < 0)
plat->bus_id = 0;
- of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr);
+ /* Default to phy auto-detection */
+ plat->phy_addr = -1;
+
+ /* "snps,phy-addr" is not a standard property. Mark it as deprecated
+ * and warn of its use. Remove this when phy node support is added.
+ */
+ if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
+ dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
sizeof(struct stmmac_mdio_bus_data),
--
1.8.5.2
^ permalink raw reply related
* [PATCH net-next v3 5/8] net: stmmac: Honor DT parameter to force DMA store and forward mode
From: Chen-Yu Tsai @ 2014-01-17 13:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389965087-21971-1-git-send-email-wens@csie.org>
"snps,force_sf_dma_mode" is documented in stmmac device tree bindings,
but is never handled by the driver.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 704a5e0..634260e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -56,6 +56,8 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
sizeof(struct stmmac_mdio_bus_data),
GFP_KERNEL);
+ plat->force_sf_dma_mode = of_property_read_bool(np, "snps,force_sf_dma_mode");
+
/*
* Currently only the properties needed on SPEAr600
* are provided. All other properties should be added
--
1.8.5.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox