From patchwork Wed Jun 18 09:09:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuninori Morimoto X-Patchwork-Id: 4374521 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id AD2CA9F314 for ; Wed, 18 Jun 2014 09:09:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CF4D9202FE for ; Wed, 18 Jun 2014 09:09:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3335B202F2 for ; Wed, 18 Jun 2014 09:09:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S964931AbaFRJJJ (ORCPT ); Wed, 18 Jun 2014 05:09:09 -0400 Received: from mail-pd0-f169.google.com ([209.85.192.169]:56710 "EHLO mail-pd0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964840AbaFRJJG (ORCPT ); Wed, 18 Jun 2014 05:09:06 -0400 Received: by mail-pd0-f169.google.com with SMTP id g10so502822pdj.14 for ; Wed, 18 Jun 2014 02:09:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:sender:message-id:from:subject:user-agent:to:cc:in-reply-to :references:mime-version:content-type; bh=nGn5FGkvs9yA/yxGPSFyoS0FJlzGsZANpaop4TygupE=; b=x2IID/CaDM1YSk01epQ+SWTgxkcl6YlVnHryeKEbkl5mglRuaSU02T5oWbYgEBL2/C hLkjPGrDoNuD4REnvjPDlh/3IBZCsaE5AGy1Z6TjDcvfC1jlgJ7lqTs4a+p8X6jACGOd RtWOHTogMzFqjonHxw8llaI4GFS4FivqmZl6lWd06HgbXWtD0jal7/Vixp27nBWZKZ6L zmoTQsVKtAVD6eTZ7Gb9OsB1r4hcvAZM03yId4wjwR1riYE2yub5VEEJCdUH28lI5U7b uoRW4cg/JGE4GOJGEc45BiW3ZtqkBrh2IeapbXNawPR1t8kXHohT9CFeY+xlPSq/NVl0 KNPQ== X-Received: by 10.68.241.68 with SMTP id wg4mr981124pbc.66.1403082546431; Wed, 18 Jun 2014 02:09:06 -0700 (PDT) Received: from remon.gmail.com (49.14.32.202.bf.2iij.net. [202.32.14.49]) by mx.google.com with ESMTPSA id fe3sm2315897pbd.66.2014.06.18.02.09.04 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Wed, 18 Jun 2014 02:09:05 -0700 (PDT) Date: Wed, 18 Jun 2014 02:09:05 -0700 (PDT) Message-ID: <87lhsuy47h.wl%kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto Subject: [PROTOTYPE 1/8] DMA: shdma: initial of common code User-Agent: Wanderlust/2.14.0 Emacs/23.3 Mule/6.0 To: Simon Cc: Kuninori Morimoto , Magnus , linux-sh@vger.kernel.org In-Reply-To: <87mwday4gg.wl%kuninori.morimoto.gx@renesas.com> References: <87oaxqy4i1.wl%kuninori.morimoto.gx@gmail.com> <87mwday4gg.wl%kuninori.morimoto.gx@renesas.com> MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Ben Dooks Add support for building shdma internal data from the device tree to allow converting the driver to be device tree enabled. It includes a helper for the of case to build the internal data used to select and filter out the DMA channels from the ID information in the device tree. Also updates the documentation for the DT case. Signed-off-by: Ben Dooks --- Documentation/devicetree/bindings/dma/shdma.txt | 23 ++- drivers/dma/sh/shdma-of.c | 174 ++++++++++++++++++++++- drivers/dma/sh/shdma.h | 29 ++++ include/dt-bindings/dma/shdma.h | 45 ++++++ 4 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 include/dt-bindings/dma/shdma.h diff --git a/Documentation/devicetree/bindings/dma/shdma.txt b/Documentation/devicetree/bindings/dma/shdma.txt index 2a3f3b8..86d5c50 100644 --- a/Documentation/devicetree/bindings/dma/shdma.txt +++ b/Documentation/devicetree/bindings/dma/shdma.txt @@ -14,9 +14,9 @@ still has to be placed under such a multiplexer node. Required properties: - compatible: should be "renesas,shdma-mux" - #dma-cells: should be <1>, see "dmas" property below +- dma-channels: number of DMA channels Optional properties (currently unused): -- dma-channels: number of DMA channels - dma-requests: number of DMA request signals * DMA controller @@ -25,6 +25,7 @@ Required properties: - compatible: should be of the form "renesas,shdma-", where should be replaced with the desired SoC model, e.g. "renesas,shdma-r8a73a4" for the system DMAC on r8a73a4 SoC + "renesas,shdma-r8a7790" for the DMAC on the R8A7790. Example: dmac: dma-multiplexer@0 { @@ -70,15 +71,33 @@ Example: }; }; +For r8a7790 and newer implementations, the information for the channel +controll register is found in the dma phandle and #dma-cells is set to 2. + + dma0: dma-mux@0 { + compatible = "renesas,shdma-mux"; + #dma-cells = <2>; + dma-channels = <20>; + dma-requests = <256>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + } + * DMA client Required properties: - dmas: a list of <[DMA multiplexer phandle] [MID/RID value]> pairs, where MID/RID values are fixed handles, specified in the SoC - manual + manual as the value that would be written into the DMASR. + + If the #dma-cells is 2, then the second value is the CHCR + register configuration for the channel. + - dma-names: a list of DMA channel names, one per "dmas" entry Example: dmas = <&dmac 0xd1 &dmac 0xd2>; dma-names = "tx", "rx"; + diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c index b4ff9d3..696716b 100644 --- a/drivers/dma/sh/shdma-of.c +++ b/drivers/dma/sh/shdma-of.c @@ -3,12 +3,15 @@ * * Copyright (C) 2013 Renesas Electronics Inc. * Author: Guennadi Liakhovetski + * Copyright (c) 2014 Codethink Limited + * Ben Dooks * * This is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. */ +#include #include #include #include @@ -16,17 +19,117 @@ #include #include #include +#include + +#include #define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan) +#include "shdma-arm.h" + +static const unsigned int arm_dma_ts_shift[] = SH_DMAE_TS_SHIFT; + +static struct sh_dmae_pdata arm_dmae_info = { + .ts_low_shift = SHDMA_ARM_TS_LOW_SHIFT, + .ts_low_mask = SHDMA_ARM_TS_LOW_BIT << SHDMA_ARM_TS_LOW_SHIFT, + .ts_high_shift = SHDMA_ARM_TS_HI_SHIFT, + .ts_high_mask = SHDMA_ARM_TS_HI_BIT << SHDMA_ARM_TS_HI_SHIFT, + .ts_shift = arm_dma_ts_shift, + .ts_shift_num = ARRAY_SIZE(arm_dma_ts_shift), + .dmaor_init = DMAOR_DME, + .chclr_present = 1, + .chclr_bitwise = 1, +}; + +struct sh_dmae_of_info shdma_arm_info = { + .pdata_template = &arm_dmae_info, + .channel_offset = 0x8000-0x20, + .channel_stride = 0x80, + .offset = 0x0, + .dmars = 0x40, + .chclr_offset = 0x80, +}; + +/* Need global index due to shdma_slave_used being global */ +static atomic_t of_slave_index = ATOMIC_INIT(0); + +struct shdma_of_client { + int index; + struct list_head node; + struct sh_dmae_slave_config cfg; +}; + +struct shdma_of_pdata { + struct sh_dmae_pdata pdata; + struct shdma_of_state *state; +}; + +struct shdma_of_state { + struct list_head of_slaves; +}; + +#define to_of_pdata(_pd) container_of(_pd, struct shdma_of_pdata, pdata) + +struct shdma_of_client *shdma_of_find_client(struct shdma_of_state *state, int id) +{ + struct shdma_of_client *client; + + list_for_each_entry(client, &state->of_slaves, node) { + if (client->cfg.slave_id == id) + return client; + } + + return NULL; +} + +struct sh_dmae_slave_config * +shdma_find_slave_of(struct sh_dmae_device *shdev, int match, int *index) +{ + struct shdma_of_pdata *pdata = to_of_pdata(shdev->pdata); + struct sh_dmae_slave_config *cfg = NULL; + struct shdma_of_client *client; + + pr_info("looking for %02x\n", match); + + client = shdma_of_find_client(pdata->state, match); + if (client) { + *index = client->index; + cfg = &client->cfg; + } + + return cfg; +} + static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { + struct platform_device *pdev = ofdma->of_dma_data; u32 id = dma_spec->args[0]; dma_cap_mask_t mask; struct dma_chan *chan; - if (dma_spec->args_count != 1) + if (dma_spec->args_count == 2) { + struct shdma_of_state *state = platform_get_drvdata(pdev); + struct shdma_of_client *client; + + client = shdma_of_find_client(state, id); + if (!client) { + client = devm_kzalloc(&pdev->dev, sizeof(*client), + GFP_KERNEL); + if (!client) + return NULL; + + client->index = atomic_inc_return(&of_slave_index); + client->cfg.slave_id = id; + client->cfg.mid_rid = id; + client->cfg.chcr = dma_spec->args[1]; + + dev_dbg(&pdev->dev, "new client %d, %02x\n", + client->index, id); + + list_add_tail(&client->node, &state->of_slaves); + } + } else if (dma_spec->args_count != 1) return NULL; dma_cap_zero(mask); @@ -41,11 +144,80 @@ static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec, return chan; } +const struct sh_dmae_pdata * +sh_dma_probe_of(struct platform_device *pdev, const struct of_device_id *ofmatch) +{ + const struct device_node *np = pdev->dev.of_node; + const struct sh_dmae_of_info *ofinf; + struct device *dev = &pdev->dev; + struct sh_dmae_pdata *pdata; + struct shdma_of_pdata *ofdata; + struct sh_dmae_channel *chan; + u32 nr_chan; + unsigned ch; + int ret; + + if (!ofmatch) + return NULL; + + ofdata = devm_kzalloc(dev, sizeof(struct shdma_of_pdata), GFP_KERNEL); + if (!ofdata) + return NULL; + + ofinf = ofmatch->data; + pdata = &ofdata->pdata; + *pdata = *ofinf->pdata_template; /* copy in template first */ + ofdata->state = dev_get_drvdata(dev->parent); + + ret = of_property_read_u32(np, "dma-channels", &nr_chan); + if (ret < 0) { + dev_err(dev, "failed to get number of channels\n"); + return NULL; + } + + chan = devm_kcalloc(dev, nr_chan, sizeof(struct sh_dmae_channel), + GFP_KERNEL); + if (!chan) { + dev_err(dev, "cannot allocate %d channels\n", nr_chan); + return NULL; + } + + pdata->channel = chan; + pdata->channel_num = nr_chan; + + dev_dbg(dev, "%d dma channels allocated\n", nr_chan); + + for (ch = 0; ch < nr_chan; ch++) { + struct sh_dmae_channel *cp = chan + ch; + u32 base = ofinf->channel_offset + ofinf->channel_stride * ch; + + cp->offset = base + ofinf->offset; + cp->dmars = base + ofinf->dmars; + cp->chclr_bit = ch; + cp->chclr_offset = ofinf->chclr_offset; + + dev_dbg(dev, "ch %d: off %08x, dmars %08x, bit %d, off %d\n", + ch, cp->offset, cp->dmars, + cp->chclr_bit, cp->chclr_offset); + } + + pdev->dev.platform_data = pdata; + return pdata; +}; + static int shdma_of_probe(struct platform_device *pdev) { const struct of_dev_auxdata *lookup = dev_get_platdata(&pdev->dev); + struct shdma_of_state *state; int ret; + state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + INIT_LIST_HEAD(&state->of_slaves); + platform_set_drvdata(pdev, state); + ret = of_dma_controller_register(pdev->dev.of_node, shdma_of_xlate, pdev); if (ret < 0) diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h index 758a57b..05b84b9 100644 --- a/drivers/dma/sh/shdma.h +++ b/drivers/dma/sh/shdma.h @@ -56,6 +56,26 @@ struct sh_dmae_desc { struct shdma_desc shdma_desc; }; +/* + * Template information for building shdma data, provided as part of the + * data field in the of_device_id structure. This is then used to build + * the platform data for the dma code. + */ +struct sh_dmae_of_info { + struct sh_dmae_pdata *pdata_template; + unsigned int channel_offset; + unsigned int channel_stride; + int offset; + int dmars; + int chclr_offset; + int chclr_bit; +}; + +extern struct sh_dmae_of_info shdma_arm_info; + +extern const struct sh_dmae_pdata *sh_dma_probe_of(struct platform_device *pdev, + const struct of_device_id *match); + #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, shdma_chan) #define to_sh_desc(lh) container_of(lh, struct sh_desc, node) #define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx) @@ -69,4 +89,13 @@ extern const struct sh_dmae_pdata r8a73a4_dma_pdata; #define r8a73a4_shdma_devid NULL #endif +#ifdef CONFIG_OF +extern struct sh_dmae_slave_config *shdma_find_slave_of(struct sh_dmae_device *shdev, int match, int *index); +#else +static inline struct sh_dmae_slave_config *shdma_find_slave_of(struct sh_dmae_device *shdev, int match, int *index) +{ + return NULL; +} +#endif + #endif /* __DMA_SHDMA_H */ diff --git a/include/dt-bindings/dma/shdma.h b/include/dt-bindings/dma/shdma.h new file mode 100644 index 0000000..0c8fc9e --- /dev/null +++ b/include/dt-bindings/dma/shdma.h @@ -0,0 +1,45 @@ +/* DMA binding definitions for SH-DMAC engines. + * + * Moved from sh_dma.h to share with device tree by Ben Dooks. + * Orignal code from: + * shdma.h: Copyright (C) 2010 Guennadi Liakhovetski + * shdma-arm.h: Copyright (C) 2013 Renesas Electronics, Inc. + * + * 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. + */ + +#define SHDMA_DM_INC 0x00004000 +#define SHDMA_DM_DEC 0x00008000 +#define SHDMA_DM_FIX 0x0000c000 +#define SHDMA_SM_INC 0x00001000 +#define SHDMA_SM_DEC 0x00002000 +#define SHDMA_SM_FIX 0x00003000 +#define SHDMA_CHCR_DE 0x00000001 +#define SHDMA_CHCR_TE 0x00000002 +#define SHDMA_CHCR_IE 0x00000004 + +/* ARM specific definitions */ + +#define SHDMA_ARM_SZ_8BIT (0) +#define SHDMA_ARM_SZ_16BIT (1) +#define SHDMA_ARM_SZ_32BIT (2) +#define SHDMA_ARM_SZ_64BIT (7) +#define SHDMA_ARM_SZ_128BIT (3) +#define SHDMA_ARM_SZ_256BIT (4) +#define SHDMA_ARM_SZ_512BIT (5) + +#define SHDMA_ARM_TS_LOW_BIT (0x3) +#define SHDMA_ARM_TS_HI_BIT (0xc) + +#define SHDMA_ARM_TS_LOW_SHIFT (3) +#define SHDMA_ARM_TS_HI_SHIFT (20 - 2) /* 2 bits for shifted low TS */ + +#define SHDMA_ARM_TS_INDEX2VAL(i) \ + ((((i) & SHDMA_ARM_TS_LOW_BIT) << SHDMA_ARM_TS_LOW_SHIFT) |\ + (((i) & SHDMA_ARM_TS_HI_BIT) << SHDMA_ARM_TS_HI_SHIFT)) + +#define SHDMA_ARM_CHCR_RX(size) (SHDMA_DM_INC | SHDMA_SM_FIX | 0x800 | SHDMA_ARM_TS_INDEX2VAL(size)) +#define SHDMA_ARM_CHCR_TX(size) (SHDMA_DM_FIX | SHDMA_SM_INC | 0x800 | SHDMA_ARM_TS_INDEX2VAL(size)) +