* [PATCH] audit: Modify a set of system calls in audit class definitions
From: AKASHI Takahiro @ 2014-01-17 8:05 UTC (permalink / raw)
To: linux-arm-kernel
Each asm-generic/audit_xx.h defines a set of system calls for respective
audit permssion class (read, write, change attribute or exec).
This patch changes two entries:
1) fchown in audit_change_attr.h
Make fchown included by its own because in asm-generic/unistd.h, for example,
fchown always exists while chown is optional. This change is necessary at
least for arm64.
2) truncate64 in audit_write.h
Add missing truncate64/ftruncate64 as well as truncate/ftruncate
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
include/asm-generic/audit_change_attr.h | 4 +++-
include/asm-generic/audit_write.h | 6 ++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h
index 89b73e5..a186553 100644
--- a/include/asm-generic/audit_change_attr.h
+++ b/include/asm-generic/audit_change_attr.h
@@ -4,9 +4,11 @@ __NR_chmod,
__NR_fchmod,
#ifdef __NR_chown
__NR_chown,
-__NR_fchown,
__NR_lchown,
#endif
+#ifdef __NR_fchown
+__NR_fchown,
+#endif
__NR_setxattr,
__NR_lsetxattr,
__NR_fsetxattr,
diff --git a/include/asm-generic/audit_write.h b/include/asm-generic/audit_write.h
index e7020c5..274575d 100644
--- a/include/asm-generic/audit_write.h
+++ b/include/asm-generic/audit_write.h
@@ -10,6 +10,12 @@ __NR_truncate,
#ifdef __NR_truncate64
__NR_truncate64,
#endif
+#ifdef __NR_ftruncate
+__NR_ftruncate,
+#endif
+#ifdef __NR_ftruncate64
+__NR_ftruncate64,
+#endif
#ifdef __NR_bind
__NR_bind, /* bind can affect fs object only in one way... */
#endif
--
1.7.9.5
^ permalink raw reply related
* [PATCH] arm64: mm: use ubfm for dcache_line_size
From: Jingoo Han @ 2014-01-17 8:04 UTC (permalink / raw)
To: linux-arm-kernel
Use 'ubfm' for the bitfield move instruction; thus, single
instruction can be used instead of two instructions, when
getting the minimum D-cache line size from CTR_EL0 register.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
arch/arm64/mm/proc-macros.S | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
index 8957b82..c31f41e 100644
--- a/arch/arm64/mm/proc-macros.S
+++ b/arch/arm64/mm/proc-macros.S
@@ -38,8 +38,7 @@
*/
.macro dcache_line_size, reg, tmp
mrs \tmp, ctr_el0 // read CTR
- lsr \tmp, \tmp, #16
- and \tmp, \tmp, #0xf // cache line size encoding
+ ubfm \tmp, \tmp, #0x16, 0x19 // cache line size encoding
mov \reg, #4 // bytes per word
lsl \reg, \reg, \tmp // actual cache line size
.endm
--
1.7.10.4
^ permalink raw reply related
* [PATCH v3] audit: Add generic compat syscall support
From: AKASHI Takahiro @ 2014-01-17 8:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389945795-4255-1-git-send-email-takahiro.akashi@linaro.org>
lib/audit.c provides a generic definition for auditing system calls.
This patch extends it for compat syscall support on bi-architectures
(32/64-bit) by adding lib/compat_audit.c when CONFIG_COMPAT enabled.
Each architecture that wants to use this must define audit_is_compat()
in asm/audit.h.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
include/linux/audit.h | 9 +++++++++
lib/Makefile | 3 +++
lib/audit.c | 17 +++++++++++++++++
lib/compat_audit.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 80 insertions(+)
create mode 100644 lib/compat_audit.c
diff --git a/include/linux/audit.h b/include/linux/audit.h
index bf1ef22..3d71949 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -78,6 +78,15 @@ extern int is_audit_feature_set(int which);
extern int __init audit_register_class(int class, unsigned *list);
extern int audit_classify_syscall(int abi, unsigned syscall);
extern int audit_classify_arch(int arch);
+#if defined(CONFIG_AUDIT_GENERIC) && defined(CONFIG_COMPAT)
+extern unsigned compat_write_class[];
+extern unsigned compat_read_class[];
+extern unsigned compat_dir_class[];
+extern unsigned compat_chattr_class[];
+extern unsigned compat_signal_class[];
+
+extern int audit_classify_compat_syscall(int abi, unsigned syscall);
+#endif
/* audit_names->type values */
#define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */
diff --git a/lib/Makefile b/lib/Makefile
index a459c31..73ea908 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -93,6 +93,9 @@ obj-$(CONFIG_TEXTSEARCH_BM) += ts_bm.o
obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o
obj-$(CONFIG_SMP) += percpu_counter.o
obj-$(CONFIG_AUDIT_GENERIC) += audit.o
+ifeq ($(CONFIG_COMPAT),y)
+obj-$(CONFIG_AUDIT_GENERIC) += compat_audit.o
+endif
obj-$(CONFIG_SWIOTLB) += swiotlb.o
obj-$(CONFIG_IOMMU_HELPER) += iommu-helper.o
diff --git a/lib/audit.c b/lib/audit.c
index 76bbed4..e29ba82 100644
--- a/lib/audit.c
+++ b/lib/audit.c
@@ -1,6 +1,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/audit.h>
+#include <asm/audit.h>
#include <asm/unistd.h>
static unsigned dir_class[] = {
@@ -30,11 +31,20 @@ static unsigned signal_class[] = {
int audit_classify_arch(int arch)
{
+#ifdef CONFIG_COMPAT
+ if (audit_is_compat(arch))
+ return 1;
+#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall)
{
+#ifdef CONFIG_COMPAT
+ if (audit_is_compat(abi))
+ return audit_classify_compat_syscall(abi, syscall);
+#endif
+
switch(syscall) {
#ifdef __NR_open
case __NR_open:
@@ -57,6 +67,13 @@ int audit_classify_syscall(int abi, unsigned syscall)
static int __init audit_classes_init(void)
{
+#ifdef CONFIG_COMPAT
+ audit_register_class(AUDIT_CLASS_WRITE_32, compat_write_class);
+ audit_register_class(AUDIT_CLASS_READ_32, compat_read_class);
+ audit_register_class(AUDIT_CLASS_DIR_WRITE_32, compat_dir_class);
+ audit_register_class(AUDIT_CLASS_CHATTR_32, compat_chattr_class);
+ audit_register_class(AUDIT_CLASS_SIGNAL_32, compat_signal_class);
+#endif
audit_register_class(AUDIT_CLASS_WRITE, write_class);
audit_register_class(AUDIT_CLASS_READ, read_class);
audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class);
diff --git a/lib/compat_audit.c b/lib/compat_audit.c
new file mode 100644
index 0000000..94f6480
--- /dev/null
+++ b/lib/compat_audit.c
@@ -0,0 +1,51 @@
+#include <linux/init.h>
+#include <linux/types.h>
+/* FIXME: this might be architecture dependent */
+#include <asm/unistd_32.h>
+
+unsigned compat_dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+unsigned compat_read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+unsigned compat_write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+unsigned compat_chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+unsigned compat_signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int audit_classify_compat_syscall(int abi, unsigned syscall)
+{
+ switch (syscall) {
+#ifdef __NR_open
+ case __NR_open:
+ return 2;
+#endif
+#ifdef __NR_openat
+ case __NR_openat:
+ return 3;
+#endif
+#ifdef __NR_socketcall
+ case __NR_socketcall:
+ return 4;
+#endif
+ case __NR_execve:
+ return 5;
+ default:
+ return 1;
+ }
+}
--
1.7.9.5
^ permalink raw reply related
* [PATCH v3] audit: generic compat system call support
From: AKASHI Takahiro @ 2014-01-17 8:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1384854235-6567-1-git-send-email-takahiro.akashi@linaro.org>
Arm64 supports 32-bit mode(AArch32) and 64-bit mode(AArch64).
To enable audit on arm64, we want to use lib/audit.c and re-work it
to support compat system calls as well without copying it under
arch sub-directory.
So this patch is mandatory for my "system call audit support for arm64"
patch. Please review it as well for better understandings.
This code was tested on armv8 fast model with 64-bit and 32-bit userland
by using modified audit-test-code.
Changes v1 -> v2:
* Specify AUDIT_CLASS_XYZ_32 instead of AUDIT_CLASS_XYZ when registering
compat syscalls (bug fix)
AKASHI Takahiro (1):
audit: Add generic compat syscall support
include/linux/audit.h | 9 +++++++++
lib/Makefile | 3 +++
lib/audit.c | 17 +++++++++++++++++
lib/compat_audit.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 80 insertions(+)
create mode 100644 lib/compat_audit.c
--
1.7.9.5
^ permalink raw reply
* [PATCH 2/2] ARM: OMAP2+: fix dpll round_rate() to actually round
From: Tomi Valkeinen @ 2014-01-17 7:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389944699-27962-1-git-send-email-tomi.valkeinen@ti.com>
omap2_dpll_round_rate() doesn't actually round the given rate, even if
the name and the description so hints. Instead it only tries to find an
exact rate match, or if that fails, return ~0 as an error.
What this basically means is that the user of the clock needs to know
what rates the dpll can support, which obviously isn't right.
This patch adds a simple method of rounding: during the iteration, the
code keeps track of the closest rate match. If no exact match is found,
the closest is returned.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
arch/arm/mach-omap2/clkt_dpll.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index 1f1708ef77bb..1b4e68dfb713 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -298,6 +298,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
struct dpll_data *dd;
unsigned long ref_rate;
const char *clk_name;
+ unsigned long diff, closest_diff = ~0;
if (!clk || !clk->dpll_data)
return ~0;
@@ -345,20 +346,26 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n",
clk_name, m, n, new_rate);
- if (target_rate == new_rate) {
+ diff = max(target_rate, new_rate) - min(target_rate, new_rate);
+
+ if (diff < closest_diff) {
+ closest_diff = diff;
+
dd->last_rounded_m = m;
dd->last_rounded_n = n;
- dd->last_rounded_rate = target_rate;
- break;
+ dd->last_rounded_rate = new_rate;
+
+ if (diff == 0)
+ break;
}
}
- if (target_rate != new_rate) {
+ if (closest_diff == ~0) {
pr_debug("clock: %s: cannot round to rate %lu\n",
clk_name, target_rate);
return ~0;
}
- return target_rate;
+ return dd->last_rounded_rate;
}
--
1.8.3.2
^ permalink raw reply related
* [PATCH 1/2] ARM: OMAP2+: fix rate prints
From: Tomi Valkeinen @ 2014-01-17 7:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389944699-27962-1-git-send-email-tomi.valkeinen@ti.com>
Printing with unsigned long rates with %ld gives wrong result if the
rate is high enough. Fix this by using %lu.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
arch/arm/mach-omap2/clkt_dpll.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index 924c230f8948..1f1708ef77bb 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -306,7 +306,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
ref_rate = __clk_get_rate(dd->clk_ref);
clk_name = __clk_get_name(hw->clk);
- pr_debug("clock: %s: starting DPLL round_rate, target rate %ld\n",
+ pr_debug("clock: %s: starting DPLL round_rate, target rate %lu\n",
clk_name, target_rate);
scaled_rt_rp = target_rate / (ref_rate / DPLL_SCALE_FACTOR);
@@ -342,7 +342,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
if (r == DPLL_MULT_UNDERFLOW)
continue;
- pr_debug("clock: %s: m = %d: n = %d: new_rate = %ld\n",
+ pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n",
clk_name, m, n, new_rate);
if (target_rate == new_rate) {
@@ -354,7 +354,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
}
if (target_rate != new_rate) {
- pr_debug("clock: %s: cannot round to rate %ld\n",
+ pr_debug("clock: %s: cannot round to rate %lu\n",
clk_name, target_rate);
return ~0;
}
--
1.8.3.2
^ permalink raw reply related
* [PATCH 0/2] ARM: OMAP2+: Fix dpll rounding
From: Tomi Valkeinen @ 2014-01-17 7:44 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
We had a problem with beaglebone black's HDMI output, where setting some clock
rates to the display PLL failed oddly. The main issue was that the dpll code
doesn't actually try to round the given rate, it only finds exact matches. That
is what these patches fix (well, the second patch).
However, there is another issue, which made finding and debugging this a bit
difficult. When the dpll round_rate failed, it returned ~0 as a rate. However,
drivers/clk/clk-divider.c, which called the round_rate, didn't realize that is
an error value, and so didn't fail but proceeded, which didn't lead to anything
good.
This series doesn't try to fix that other issue.
There's also a third issue that I think happens more easily after this series
is applied: there are no checks for maximum rate for the DPLLs. I think the
maximum rate is generally 2GHz, but at least my BeagleBone Black refused to
lock the display PLL with anything over 1.3GHz-1.4GHz.
High PLL clock rates happen quite easily with a setup where there's a DPLL and
an additional divider, handled by clk-divider.c. When the clk-divider.c is
asked to provide a clock rate (that could be quite low), it'll try to find a
good clock from its parent, the DPLL, that it can divide down to the requested
clock. As the DPLL driver is only limited by its multiplier and divider
register widths, it'll gladly offer 2GHz+ clocks, which eventually will fail as
they can't be locked.
So this series is only a partial fix for the clock issues. Also, while the
board debugged was BB Black, I think the same code is used and thus the same
issues are present on all (or most) omap2+ SoCs.
Tomi
Tomi Valkeinen (2):
ARM: OMAP2+: fix rate prints
ARM: OMAP2+: fix dpll round_rate() to actually round
arch/arm/mach-omap2/clkt_dpll.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
--
1.8.3.2
^ permalink raw reply
* [PATCH v2 1/2] i2c: qup: Add device tree bindings information
From: Ivan T. Ivanov @ 2014-01-17 7:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116232047.GK30911@sonymobile.com>
Hi,
On Thu, 2014-01-16 at 15:20 -0800, Bjorn Andersson wrote:
> On Tue 14 Jan 00:57 PST 2014, Ivan T. Ivanov wrote:
>
> >
> > Thanks Bjorn,
> >
> > I have prepared second version, but never send it out :-).
> > One thing suggested by Mark was missed in this version.
>
> Yeah, Mattew told me you we're assigned to other things and asked me to send
> out an update as I had gotten it to work on our boards.
>
> I did modify the wording of most of these to match how it is written in the
> other Qualcomm definitions.
>
> @Mark, would you rather have me change this to your suggested wording?
>
> >
> >
> > On Mon, 2014-01-13 at 16:30 -0800, Bjorn Andersson wrote:
> > > From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
> > >
> [snip]
> > > + - clocks: Should contain the core clock and the AHB clock.
> >
> > + - clocks: a list of phandle + clock-specifier pairs for each entry in
> > + clock-names
> >
>
> This is in line with how it's written in other drivers, so if the DT
> maintainers doesn't disagree I would like to keep phandles out of the
> description. This specific line is a verbatime copy of the msm_serial
> documentation (same block, different mode)...
>
> > > + - clock-names: Should be "core" for the core clock and "iface" for the
> > > + AHB clock.
> > > +
> > > + - #address-cells: Should be <1> Address cells for i2c device address
> > > + - #size-cells: Should be <0> as i2c addresses have no size component
> > > +
> > > +Optional properties:
> > > + - clock-frequency: Should specify the desired i2c bus clock frequency in Hz,
> > > + default is 100kHz if omitted.
> > > +
> > > +Child nodes should conform to i2c bus binding.
> > > +
> > > +Example:
> > > +
> > > + i2c2: i2c at f9924000 {
> > > + compatible = "qcom,i2c-qup";
> > > + reg = <0xf9924000 0x1000>;
> > > + interrupts = <0 96 0>;
> > > +
> > > + clocks = <&gcc_blsp1_qup2_i2c_apps_clk>, <&gcc_blsp1_ahb_clk>;
> >
> > In the light of the latest patches from Stephen, this could be
> >
> > + clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
>
> Yes, that's exactly what I have in my dts. However as this is just an example
> I didn't feel it was worth tainting the documentation with all those capital
> letters ;)
> So unless DT maintainers disagree I would like to just keep it as an example.
Until, we get some meaningful board DTS files for Qualcomm platforms, it
is easy for people which will like to test or use drivers to just copy
and paste these definitions.
Regards,
Ivan
>
> Regards,
> Bjorn
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Kconfig errors
From: Prabhakar Lad @ 2014-01-17 7:37 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Linux-next branch I see following errors for davinci_all_defconfig
& da8xx_omapl_defconfig configs,
arch/arm/Kconfig:1966:error: recursive dependency detected!
arch/arm/Kconfig:1966: symbol ZBOOT_ROM depends on AUTO_ZRELADDR
arch/arm/Kconfig:2154: symbol AUTO_ZRELADDR is selected by ZBOOT_ROM
#
# configuration written to .config
#
Regards,
--Prabhakar Lad
^ permalink raw reply
* [PATCH v4 0/7] mtd: spi-nor: add a new framework for SPI NOR
From: sourav @ 2014-01-17 7:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAD6G_RR7vgmHGFj4GqwCYLQQO6tbsZAeCzVBivbcxnCGSwgiWQ@mail.gmail.com>
On Friday 17 January 2014 12:36 PM, Jagan Teki wrote:
> On Fri, Jan 17, 2014 at 7:32 AM, Huang Shijie<b32955@freescale.com> wrote:
>> On Thu, Jan 16, 2014 at 03:09:13PM +0530, Jagan Teki wrote:
>>>>>> After this patch, the layer is like:
>>>>>> MTD
>>>>>> ------------------------
>>>>>> spi-nor
>>>>>> ------------------------
>>>>>> m25p80
>>>>>> ------------------------
>>>>>> spi bus driver
>>>>>> ------------------------
>>>>>> SPI NOR chip
>>> Just for looking on your new framework, is that above link correct.
>>> I guess it should be MTD -- m25p80 -- spi-nor -- spi bus driver -- SPI NOR chip
>> I do not think so.
>> The spi-nor layer does not contact with the spi bus driver directly.
> Yes - now I understand the flow from seeing the code.
> With your new framework
> 1. not an exact spi-nor
> have one controller driver at drivers/spi/* will register to spi core.
> have m25p80.c will scan the flash details from
> drivers/mtd/spi-nor/spi-nor.c and
> m25p80 will register the MTD core and for transfer calls m25p80
> will calls spi core.
> 2. spi-nor style
> have one controller driver at drivers/mtd/spi-nor/fsl-quadspi.c
> will register MTD core
> and scan the flash details from drivers/mtd/spi-nor/spi-nor.c and
> for transfer will calls
> through direct writel and readl with cmd+data fashion.
>
> Correct me If my understanding was wrong.
>>>>> 3) Can you explain your framework precisely take an example of like
>>>>> spi_controller_A with spi_flash_A
>>>>> and qspi_controller_B and qspi_flash_B - how will this new framework
>>>>> operates.
>>>>>
>>>> The framework is just cloned from the m25p80.c, and extract the common code,
>>>> and provides more
>>>> hooks such as
>>>>
>>>> @prepare/unpreare: used to do some work before or after the
>>>> read/write/erase/lock/unlock.
>>>> @read_xfer/write_xfer: We can use these two hooks to code all
>>>> the following hooks if the driver tries to implement them
>>>> by itself.
>>>> @read_reg: used to read the registers, such as read status register,
>>>> read configure register.
>>>> @write_reg: used to write the registers, such as write enable,
>>>> erase sector.
>>>> @read_id: read out the ID info.
>>>> @wait_till_ready: wait till the NOR becomes ready.
>>>> @read: read out the data from the NOR.
>>>> @write: write data to the NOR.
>>>> @erase: erase a sector of the NOR.
>>>>
>>> My basic question is like I have a qspi spi controller in my SOC and I
>>> designed two boards B1 and B2
>> okay.
>>
>>> B1 with quad spi controller connected with non-flash as a slave and B2
>>> with quad spi controller connected
>>> with quad flash as a slave.
>> You can use the framework for B2. But for B1, you should not use the framework,
>> since this framework is just for the SPI-NOR. If you do not connected with
>> a NOR, i think it's better to code another driver for your controller.
> Means we have two separate controller drivers for same controller one
> with spi-nor and
> another with spi is it?
>
> Do you think this is a good idea, I understand you have a complete and
> well guided new
> spi-nor framework.
>
I dont think its a good idea to support a single controller with two
drivers for two different usecase.
^ permalink raw reply
* [PATCH v4 0/7] mtd: spi-nor: add a new framework for SPI NOR
From: Jagan Teki @ 2014-01-17 7:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140117020226.GB27652@shlinux2.ap.freescale.net>
On Fri, Jan 17, 2014 at 7:32 AM, Huang Shijie <b32955@freescale.com> wrote:
> On Thu, Jan 16, 2014 at 03:09:13PM +0530, Jagan Teki wrote:
>> >>> After this patch, the layer is like:
>> >>> MTD
>> >>> ------------------------
>> >>> spi-nor
>> >>> ------------------------
>> >>> m25p80
>> >>> ------------------------
>> >>> spi bus driver
>> >>> ------------------------
>> >>> SPI NOR chip
>>
>> Just for looking on your new framework, is that above link correct.
>> I guess it should be MTD -- m25p80 -- spi-nor -- spi bus driver -- SPI NOR chip
>
> I do not think so.
> The spi-nor layer does not contact with the spi bus driver directly.
Yes - now I understand the flow from seeing the code.
With your new framework
1. not an exact spi-nor
have one controller driver at drivers/spi/* will register to spi core.
have m25p80.c will scan the flash details from
drivers/mtd/spi-nor/spi-nor.c and
m25p80 will register the MTD core and for transfer calls m25p80
will calls spi core.
2. spi-nor style
have one controller driver at drivers/mtd/spi-nor/fsl-quadspi.c
will register MTD core
and scan the flash details from drivers/mtd/spi-nor/spi-nor.c and
for transfer will calls
through direct writel and readl with cmd+data fashion.
Correct me If my understanding was wrong.
>
>> >> 3) Can you explain your framework precisely take an example of like
>> >> spi_controller_A with spi_flash_A
>> >> and qspi_controller_B and qspi_flash_B - how will this new framework
>> >> operates.
>> >>
>> > The framework is just cloned from the m25p80.c, and extract the common code,
>> > and provides more
>> > hooks such as
>> >
>> > @prepare/unpreare: used to do some work before or after the
>> > read/write/erase/lock/unlock.
>> > @read_xfer/write_xfer: We can use these two hooks to code all
>> > the following hooks if the driver tries to implement them
>> > by itself.
>> > @read_reg: used to read the registers, such as read status register,
>> > read configure register.
>> > @write_reg: used to write the registers, such as write enable,
>> > erase sector.
>> > @read_id: read out the ID info.
>> > @wait_till_ready: wait till the NOR becomes ready.
>> > @read: read out the data from the NOR.
>> > @write: write data to the NOR.
>> > @erase: erase a sector of the NOR.
>> >
>>
>> My basic question is like I have a qspi spi controller in my SOC and I
>> designed two boards B1 and B2
>
> okay.
>
>> B1 with quad spi controller connected with non-flash as a slave and B2
>> with quad spi controller connected
>> with quad flash as a slave.
> You can use the framework for B2. But for B1, you should not use the framework,
> since this framework is just for the SPI-NOR. If you do not connected with
> a NOR, i think it's better to code another driver for your controller.
Means we have two separate controller drivers for same controller one
with spi-nor and
another with spi is it?
Do you think this is a good idea, I understand you have a complete and
well guided new
spi-nor framework.
--
Thanks,
Jagan.
^ permalink raw reply
* [PATCH v7 0/2] ohci and ehci-platform clks, phy and dt support
From: Florian Fainelli @ 2014-01-17 7:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <Pine.LNX.4.44L0.1401151525400.1587-100000@iolanthe.rowland.org>
Le mercredi 15 janvier 2014, 15:26:21 Alan Stern a ?crit :
> On Wed, 15 Jan 2014, Hans de Goede wrote:
> > Hi All,
> >
> > This version of my ohci and ehci-platform clks, phy and dt support
> > patch-set, really fixes the 2 small bugs Alan found.
>
> All okay -- this time I can't find anything to complain about. :-)
There is one minor issue; which is that the ehci binding claims the driver
supports the following optional boolean properties:
- big-endian-regs : boolean, set this for hcds with big-endian registers
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
while it does not (yet) so this is misleading. Can we at get that fixed before
merging? Copy pasting the PPC ehci driver should do the job.
--
Florian
^ permalink raw reply
* [PATCH v4 0/7] mtd: spi-nor: add a new framework for SPI NOR
From: Huang Shijie @ 2014-01-17 6:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAD6G_RR7vgmHGFj4GqwCYLQQO6tbsZAeCzVBivbcxnCGSwgiWQ@mail.gmail.com>
On Fri, Jan 17, 2014 at 12:36:08PM +0530, Jagan Teki wrote:
> >> My basic question is like I have a qspi spi controller in my SOC and I
> >> designed two boards B1 and B2
> >
> > okay.
> >
> >> B1 with quad spi controller connected with non-flash as a slave and B2
> >> with quad spi controller connected
> >> with quad flash as a slave.
> > You can use the framework for B2. But for B1, you should not use the framework,
> > since this framework is just for the SPI-NOR. If you do not connected with
> > a NOR, i think it's better to code another driver for your controller.
>
> Means we have two separate controller drivers for same controller one
> with spi-nor and
> another with spi is it?
Take drivers/spi/spi-imx.c for example, if you connect a NOR to it, you only
need to add a NOR device node in the device tree. In the probe, it will call
the m25p80.c to probe the NOR device.
But if we connect other device to it. you should set another device node for it.
I am not sure if your controller driver can works as the spi-imx.c
thanks
Huang Shijie
^ permalink raw reply
* [PATCH RFC 6/6] ARM: sun7i: cubietruck: enable bluetooth module
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
The CubieTruck has an AMPAK AP6210 WiFi+Bluetooth module. The
Bluetooth part is a BCM20710 device connected to UART2 in the
A20 SoC.
The IC also requires a 32.768 KHz low power clock input for
proper auto-detection of the main clock, and power enable and
wake signals via GPIO.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 37 ++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index c8b3ea9..f172a8f 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -78,6 +78,20 @@
allwinner,drive = <0>;
allwinner,pull = <2>;
};
+
+ bt_pwr_pin: bt_pwr_pin at 0 {
+ allwinner,pins = "PH18";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ bt_wake_pin: bt_wake_pin at 0 {
+ allwinner,pins = "PH24";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
uart0: serial at 01c28000 {
@@ -86,6 +100,12 @@
status = "okay";
};
+ uart2: serial at 01c28800 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins_a>;
+ status = "okay";
+ };
+
gmac: ethernet at 01c50000 {
pinctrl-names = "default";
pinctrl-0 = <&gmac_pins_rgmii>;
@@ -157,4 +177,21 @@
gpio = <&pio 7 3 0>;
};
};
+
+ rfkill-switches {
+ compatible = "simple-bus";
+ pinctrl-names = "default";
+
+ rfkill_bt {
+ compatible = "rfkill-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bt_pwr_pin>, <&clk_out_a_pins>;
+ rfkill-name = "bt";
+ rfkill-type = <2>;
+ bt_shutdown-gpios = <0>, <&pio 7 18 0>; /* PH18 */
+ bt_reset-gpios = <&pio 7 24 0>; /* PH24 */
+ clocks = <&clk_out_a>;
+ clock-frequency = <32768>;
+ };
+ };
};
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 5/6] net: rfkill: gpio: add clock-frequency device tree property
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
Some devices, such as Broadcom Bluetooth devices, require a specific
clock rate for the clock tied to the rfkill device. Add clock-frequency
property so we can specify this from the device tree.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt | 2 ++
net/rfkill/rfkill-gpio.c | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
index 8a07ea4..8b8db0a 100644
--- a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
+++ b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
@@ -13,6 +13,7 @@ NAME_reset-gpios, or both, must be defined.
Optional properties:
- clocks : phandle to clock to enable/disable
+- clock-frequency : clock rate to set for the given clock
Example:
@@ -23,4 +24,5 @@ Example:
bluetooth_shutdown-gpios = <0>, <&pio 7 18 0>;
bluetooth_reset-gpios = <&pio 7 24 0>;
clocks = <&clk_out_a>;
+ clock-frequency = <32678>;
};
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 48381a8..3092681 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -40,6 +40,7 @@ struct rfkill_gpio_data {
char *reset_name;
char *shutdown_name;
struct clk *clk;
+ int clk_frequency;
bool clk_enabled;
};
@@ -92,6 +93,7 @@ static int rfkill_gpio_dt_probe(struct device *dev,
rfkill->name = np->name;
of_property_read_string(np, "rfkill-name", &rfkill->name);
of_property_read_u32(np, "rfkill-type", &rfkill->type);
+ of_property_read_u32(np, "clock-frequency", &rfkill->clk_frequency);
return 0;
}
@@ -138,6 +140,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
snprintf(rfkill->shutdown_name, len + 10, "%s_shutdown", rfkill->name);
rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
+ if (!IS_ERR(rfkill->clk) && rfkill->clk_frequency > 0)
+ clk_set_rate(rfkill->clk, rfkill->clk_frequency);
gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0);
if (!IS_ERR(gpio)) {
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 4/6] net: rfkill: gpio: add device tree support
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../devicetree/bindings/rfkill/rfkill-gpio.txt | 26 ++++++++++++++++++++++
net/rfkill/rfkill-gpio.c | 23 +++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
diff --git a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
new file mode 100644
index 0000000..8a07ea4
--- /dev/null
+++ b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
@@ -0,0 +1,26 @@
+GPIO controlled RFKILL devices
+
+Required properties:
+- compatible : Must be "rfkill-gpio".
+- rfkill-name : Name of RFKILL device
+- rfkill-type : Type of RFKILL device: 1 for WiFi, 2 for BlueTooth
+- NAME_shutdown-gpios : GPIO phandle to shutdown control
+ (phandle must be the second)
+- NAME_reset-gpios : GPIO phandle to reset control
+
+NAME must match the rfkill-name property. NAME_shutdown-gpios or
+NAME_reset-gpios, or both, must be defined.
+
+Optional properties:
+- clocks : phandle to clock to enable/disable
+
+Example:
+
+ rfkill_bt: rfkill at 0 {
+ compatible = "rfkill-gpio";
+ rfkill-name = "bluetooth";
+ rfkill-type = <2>;
+ bluetooth_shutdown-gpios = <0>, <&pio 7 18 0>;
+ bluetooth_reset-gpios = <&pio 7 24 0>;
+ clocks = <&clk_out_a>;
+ };
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 3084fa3..48381a8 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
#include <linux/rfkill-gpio.h>
@@ -83,6 +84,18 @@ static int rfkill_gpio_acpi_probe(struct device *dev,
return 0;
}
+static int rfkill_gpio_dt_probe(struct device *dev,
+ struct rfkill_gpio_data *rfkill)
+{
+ struct device_node * np = dev->of_node;
+
+ rfkill->name = np->name;
+ of_property_read_string(np, "rfkill-name", &rfkill->name);
+ of_property_read_u32(np, "rfkill-type", &rfkill->type);
+
+ return 0;
+}
+
static int rfkill_gpio_probe(struct platform_device *pdev)
{
struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
@@ -100,6 +113,10 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
if (ret)
return ret;
+ } else if (&pdev->dev.of_node) {
+ ret = rfkill_gpio_dt_probe(&pdev->dev, rfkill);
+ if (ret)
+ return ret;
} else if (pdata) {
clk_name = pdata->power_clk_name;
rfkill->name = pdata->name;
@@ -189,6 +206,11 @@ static const struct acpi_device_id rfkill_acpi_match[] = {
{ },
};
+static const struct of_device_id rfkill_of_match[] = {
+ { .compatible = "rfkill-gpio", },
+ {},
+};
+
static struct platform_driver rfkill_gpio_driver = {
.probe = rfkill_gpio_probe,
.remove = rfkill_gpio_remove,
@@ -196,6 +218,7 @@ static struct platform_driver rfkill_gpio_driver = {
.name = "rfkill_gpio",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rfkill_acpi_match),
+ .of_match_table = of_match_ptr(rfkill_of_match),
},
};
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 3/6] net: rfkill: gpio: fix reversed clock enable state
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
rfkill-gpio has clk_enabled = blocked, which is true when rfkill
blocks the device. This results in calling clock enable/disable at
the wrong time. Reversing the value fixes this.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
net/rfkill/rfkill-gpio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index c7081b7..3084fa3 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -59,7 +59,7 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
gpiod_set_value(rfkill->shutdown_gpio, 1);
}
- rfkill->clk_enabled = blocked;
+ rfkill->clk_enabled = !blocked;
return 0;
}
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 2/6] net: rfkill: gpio: use clk_prepare_enable/clk_disable_unprepare
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
rfkill-gpio calls clk_enable() without first calling clk_prepare(),
resulting in a warning and no effect. Switch to clk_prepare_enable()
and clk_disable_unprepare.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
net/rfkill/rfkill-gpio.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 97ec12a..c7081b7 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -51,10 +51,10 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
gpiod_set_value(rfkill->shutdown_gpio, 0);
gpiod_set_value(rfkill->reset_gpio, 0);
if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
- clk_disable(rfkill->clk);
+ clk_disable_unprepare(rfkill->clk);
} else {
if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
- clk_enable(rfkill->clk);
+ clk_prepare_enable(rfkill->clk);
gpiod_set_value(rfkill->reset_gpio, 1);
gpiod_set_value(rfkill->shutdown_gpio, 1);
}
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 1/6] net: rfkill: gpio: fix gpio name buffer size off by 1
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389941251-32692-1-git-send-email-wens@csie.org>
snprintf should be passed the complete size of the buffer, including
the space for '\0'. The previous code resulted in the *_reset and
*_shutdown strings being truncated.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
net/rfkill/rfkill-gpio.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index bd2a5b9..97ec12a 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -117,8 +117,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
if (!rfkill->shutdown_name)
return -ENOMEM;
- snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
- snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
+ snprintf(rfkill->reset_name, len + 7 , "%s_reset", rfkill->name);
+ snprintf(rfkill->shutdown_name, len + 10, "%s_shutdown", rfkill->name);
rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
--
1.8.5.2
^ permalink raw reply related
* [PATCH RFC 0/6] net: rfkill: gpio: Add device tree support
From: Chen-Yu Tsai @ 2014-01-17 6:47 UTC (permalink / raw)
To: linux-arm-kernel
Hi everyone,
This patch series adds device tree support to rfkill-gpio, and
fixes some issues I ran into. This is so we can define and control
RF devices through the device tree, such as the Broadcom BCM20710
UART-based Bluetooth device found on the CubieTruck,
The CubieTruck uses a non-default clock rate oscillator for the
BCM20710 device. As the datasheet states, a precise 32.768 KHz
low power clock must be provided at power on for the device to
detect the correct clock rate of the main oscillator. Hence the
need for the "clock-frequency" property.
The device tree bindings aren't pretty. They are the result of how
gpiod_find was implemented: of_gpiod_find includes con_id in the
DT property name; acpi_gpiod_find ignores it and only uses the index.
A more elegant DT binding would mean splitting the gpio lookup code
path in rfkill-gpio, which would be more like rfkill-gpio prior to
the descriptor-based GPIO patch.
I am aware there is a need for similar functionality for SDIO devices,
which the CubieTruck has as well. A mail thread [1] started yesterday
indicated that generic SDIO DT support was the way to go. I don't know
if that could be applied to UART-based devices though.
[1] http://www.spinics.net/lists/arm-kernel/msg301182.html
The series depends on
[PATCH v3 2/6] net: rfkill: gpio: convert to descriptor-based GPIO interface
which has been applied through the GPIO tree.
The last patch depends on
ARM: dts: sun7i: add pin muxing options for UART2
which I sent earlier this week.
Comments, please?
Cheers,
ChenYu
Chen-Yu Tsai (6):
net: rfkill: gpio: fix gpio name buffer size off by 1
net: rfkill: gpio: use clk_prepare_enable/clk_disable_unprepare
net: rfkill: gpio: fix reversed clock enable state
net: rfkill: gpio: add device tree support
net: rfkill: gpio: add clock-frequency device tree property
ARM: sun7i: cubietruck: enable bluetooth module
.../devicetree/bindings/rfkill/rfkill-gpio.txt | 28 ++++++++++++++++
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 37 ++++++++++++++++++++++
net/rfkill/rfkill-gpio.c | 37 +++++++++++++++++++---
3 files changed, 97 insertions(+), 5 deletions(-)
create mode 100644 Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt
--
1.8.5.2
^ permalink raw reply
* [PATCH] ARM: dts: imx6: Add DFI FS700-M60 board support
From: Shawn Guo @ 2014-01-17 6:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389876354-24341-1-git-send-email-s.hauer@pengutronix.de>
On Thu, Jan 16, 2014 at 01:45:54PM +0100, Sascha Hauer wrote:
> diff --git a/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi b/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi
> new file mode 100644
> index 0000000..ca3c748
> --- /dev/null
> +++ b/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi
> @@ -0,0 +1,145 @@
> +/ {
> + regulators {
> + compatible = "simple-bus";
> +
> + dummy_reg: fixed at 0 {
regulator at 0
> + compatible = "regulator-fixed";
reg = <0>;
> + regulator-name = "dummy-supply";
> + };
> +
> + reg_usb_otg_vbus: usb_otg_vbus {
regulator at 1
> + compatible = "regulator-fixed";
reg = <1>;
> + regulator-name = "usb_otg_vbus";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + gpio = <&gpio3 22 0>;
> + enable-active-high;
> + };
> + };
> +
> + chosen {
> + linux,stdout-path = &uart1;
> + };
> +};
> +
> +&ecspi3 {
> + fsl,spi-num-chipselects = <1>;
> + cs-gpios = <&gpio4 24 0>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_ecspi3>;
> + status = "okay";
> +
> + flash: m25p80 at 0 {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "sst,sst25vf040b", "m25p80";
> + spi-max-frequency = <20000000>;
> + reg = <0>;
> + };
> +};
> +
> +&fec {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_enet>;
> + status = "okay";
> + phy-mode = "rgmii";
> +};
> +
> +&iomuxc {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_hog>;
> +
> + imx6qdl-dfi-fs700-m60 {
> + pinctrl_hog: hoggrp {
> + fsl,pins = <
> + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000
> + MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 /* PMIC irq */
> + MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000 /* MAX11801 irq */
> + MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x000030b0 /* Backlight enable */
> + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 /* SD2 card detect */
Can we minimize the pins in hog group by moving the client device's pins
into device's pin entry, just like what you do for
MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24?
> + >;
> + };
> +
> + pinctrl_enet: enetgrp {
> + fsl,pins = <MX6QDL_ENET_PINGRP1>;
> + };
> +
> + pinctrl_i2c2: i2c2grp {
> + fsl,pins = <MX6QDL_I2C2_PINGRP1>;
> + };
> +
> + pinctrl_uart1: uart1grp {
> + fsl,pins = <MX6QDL_UART1_PINGRP1>;
> + };
> +
> + pinctrl_usbotg: usbotggrp {
> + fsl,pins = <MX6QDL_USBOTG_PINGRP2>;
> + };
> +
> + pinctrl_usdhc2: usdhc2grp {
> + fsl,pins = <MX6QDL_USDHC2_PINGRP_D4>;
> + };
> +
> + pinctrl_usdhc3: usdhc3grp {
> + fsl,pins = <MX6QDL_USDHC3_PINGRP_D4>;
> + };
> +
> + pinctrl_usdhc4: usdhc4grp {
> + fsl,pins = <MX6QDL_USDHC4_PINGRP_D8>;
> + };
> + };
> +
> + ecspi3 {
> + pinctrl_ecspi3: ecspi3_csgrp {
s/ecspi3_csgrp/ecspi3grp
And just move the node into imx6qdl-dfi-fs700-m60?
Shawn
> + fsl,pins = <
> + MX6QDL_ECSPI3_PINGRP1
> + MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000 /* SPI NOR chipselect */
> + >;
> + };
> + };
> +};
> +
> +&i2c2 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_i2c2>;
> + status = "okay";
> +};
> +
> +&uart1 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_uart1>;
> + status = "okay";
> +};
> +
> +&usbh1 {
> + status = "okay";
> +};
> +
> +&usbotg {
> + vbus-supply = <®_usb_otg_vbus>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usbotg>;
> + disable-over-current;
> + dr_mode = "host";
> + status = "okay";
> +};
> +
> +&usdhc2 { /* module slot */
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usdhc2>;
> + cd-gpios = <&gpio2 2 0>;
> + status = "okay";
> +};
> +
> +&usdhc3 { /* baseboard slot */
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usdhc3>;
> +};
> +
> +&usdhc4 { /* eMMC */
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usdhc4>;
> + bus-width = <8>;
> + non-removable;
> + status = "okay";
> +};
> --
> 1.8.5.2
>
^ permalink raw reply
* [BUG] FL1009: xHCI host not responding to stop endpoint command.
From: Arnaud Ebalard @ 2014-01-17 6:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116185044.GC18392@xanatos>
Hi,
Sarah Sharp <sarah.a.sharp@linux.intel.com> writes:
>> ... AFAICT, this is exactly what commit 35773dac5f86 does and reverting
>> it does not help. If I am mistaken, can you point which part you want me
>> to remove in the code to test?
>>
>> I am slowly starting to see a bisect session coming ;-)
>
> Try reverting commit 60e102ac73cd40069d077014c93c86dc7205cb68.
AFAICT, this commit does not exist in master (Linus tree), i.e. it is
not in 3.13.0-rc8.
> That was causing issues with another Fresco Logic host. If that
> doesn't help, then yes, please git bisect.
I have started a bisect session yesterday. I will try and finish it this
evening.
Cheers,
a+
^ permalink raw reply
* [PATCH v4 net-next 2/4] sh_eth: Add support for r7s72100
From: Simon Horman @ 2014-01-17 6:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D7FC8D.7000800@cogentembedded.com>
On Thu, Jan 16, 2014 at 07:36:45PM +0400, Sergei Shtylyov wrote:
> Hello.
>
> On 16-01-2014 4:49, 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.
>
> [...]
> >>>>>diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
> >>>>>index 0fe35b7..0bcde90 100644
> >>>>>--- a/drivers/net/ethernet/renesas/sh_eth.h
> >>>>>+++ b/drivers/net/ethernet/renesas/sh_eth.h
> >>[...]
> >>>>>@@ -191,6 +192,7 @@ enum DMAC_M_BIT {
> >>>>> /* EDTRR */
> >>>>> enum DMAC_T_BIT {
> >>>>> EDTRR_TRNS_GETHER = 0x03,
> >>>>>+ EDTRR_TRNS_RZ_ETHER = 0x03,
>
> >>>> I doubt we need a special case here. You didn't introduce one for
> >>>>the software reset bits.
>
> >>>True, but RZ is not Gigabit. So I think we either need two names
> >>>or to choose a more generic name.
>
> >> Well, R7S72100 manual calls these bits just TR[1:0]. Don't know
> >>what SoCs having Gigabit call it in the manuals...
>
> >>>>> EDTRR_TRNS_ETHER = 0x01,
>
> >> R-Car manuals seem to call the bit TRNS (as well as the
> >>prehistoric SH manuals probably). Perhaps we could use that
> >>difference, TRNS vs TR, don't know...
>
> >Perhaps we should just leave it as-is, using EDTRR_TRNS_GETHER and
> >EDTRR_TRNS_RZ_ETHER, after all.
>
> No, I liked your last version more. At least it's more
> consistent, not adding separate values for either TR[1:0] or soft
> reset bits.
>
> >At least until we can think of a better names :)
>
> I doubt we can come up with something better.
>
> WBR, Sergei
>
^ permalink raw reply
* [PATCH] dma: imx-sdma: remove firmare not found warning
From: Shawn Guo @ 2014-01-17 6:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389887186-6146-1-git-send-email-s.hauer@pengutronix.de>
On Thu, Jan 16, 2014 at 04:46:26PM +0100, Sascha Hauer wrote:
> When a firmware cannot be found for the SDMA engine then we can
> continue with the inernal ROM firmware.
>
> The meaning of this message is frequently asked for and the usual
> answer is that this message is of no relevance, so just make it
> dev_dbg() and do not alienate the users anymore.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Vinod Koul <vinod.koul@intel.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> drivers/dma/imx-sdma.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
> index c75679d..fe57a42 100644
> --- a/drivers/dma/imx-sdma.c
> +++ b/drivers/dma/imx-sdma.c
> @@ -1259,7 +1259,10 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
> unsigned short *ram_code;
>
> if (!fw) {
> - dev_err(sdma->dev, "firmware not found\n");
> + dev_dbg(sdma->dev, "firmware not found\n");
> + /*
> + * In this case we just use the ROM firmware.
> + */
> return;
> }
>
> --
> 1.8.5.2
>
^ permalink raw reply
* [PATCHv10 2/2] dma: Add Freescale eDMA engine driver support
From: Jingchang Lu @ 2014-01-17 6:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389938684-29467-1-git-send-email-b35083@freescale.com>
Add Freescale enhanced direct memory(eDMA) controller support.
This module can be found on Vybrid and LS-1 SoCs.
Signed-off-by: Alison Wang <b18965@freescale.com>
Signed-off-by: Jingchang Lu <b35083@freescale.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
changes in v10:
define fsl_edma_mutex in fsl_edma_engine instead of global.
minor changes of binding description.
changes in v9:
define endian's operating functions instead of macro definition.
remove the filter function, using dma_get_slave_channel instead.
changes in v8:
change the edma driver according eDMA dts change.
add big-endian and little-endian handling.
no changes in v4 ~ v7.
changes in v3:
add vf610 edma dt-bindings namespace with prefix VF610_*.
changes in v2:
using generic dma-channels property instead of fsl,dma-channels.
Documentation/devicetree/bindings/dma/fsl-edma.txt | 76 ++
drivers/dma/Kconfig | 10 +
drivers/dma/Makefile | 1 +
drivers/dma/fsl-edma.c | 956 +++++++++++++++++++++
4 files changed, 1043 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/fsl-edma.txt
create mode 100644 drivers/dma/fsl-edma.c
diff --git a/Documentation/devicetree/bindings/dma/fsl-edma.txt b/Documentation/devicetree/bindings/dma/fsl-edma.txt
new file mode 100644
index 0000000..191d7bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/fsl-edma.txt
@@ -0,0 +1,76 @@
+* Freescale enhanced Direct Memory Access(eDMA) Controller
+
+ The eDMA channels have multiplex capability by programmble memory-mapped
+registers. channels are split into two groups, called DMAMUX0 and DMAMUX1,
+specific DMA request source can only be multiplexed by any channel of certain
+group, DMAMUX0 or DMAMUX1, but not both.
+
+* eDMA Controller
+Required properties:
+- compatible :
+ - "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC
+- reg : Specifies base physical address(s) and size of the eDMA registers.
+ The 1st region is eDMA control register's address and size.
+ The 2nd and the 3rd regions are programmable channel multiplexing
+ control register's address and size.
+- interrupts : A list of interrupt-specifiers, one for each entry in
+ interrupt-names.
+- interrupt-names : Should contain:
+ "edma-tx" - the transmission interrupt
+ "edma-err" - the error interrupt
+- #dma-cells : Must be <2>.
+ The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1).
+ Specific request source can only be multiplexed by specific channels
+ group called DMAMUX.
+ The 2nd cell specifies the request source(slot) ID.
+ See the SoC's reference manual for all the supported request sources.
+- dma-channels : Number of channels supported by the controller
+- clock-names : A list of channel group clock names. Should contain:
+ "dmamux0" - clock name of mux0 group
+ "dmamux1" - clock name of mux1 group
+- clocks : A list of phandle and clock-specifier pairs, one for each entry in
+ clock-names.
+
+Optional properties:
+- big-endian: If present registers and hardware scatter/gather descriptors
+ of the eDMA are implemented in big endian mode, otherwise in little
+ mode.
+
+
+Examples:
+
+edma0: dma-controller at 40018000 {
+ #dma-cells = <2>;
+ compatible = "fsl,vf610-edma";
+ reg = <0x40018000 0x2000>,
+ <0x40024000 0x1000>,
+ <0x40025000 0x1000>;
+ interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
+ <0 9 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "edma-tx", "edma-err";
+ dma-channels = <32>;
+ clock-names = "dmamux0", "dmamux1";
+ clocks = <&clks VF610_CLK_DMAMUX0>,
+ <&clks VF610_CLK_DMAMUX1>;
+};
+
+
+* DMA clients
+DMA client drivers that uses the DMA function must use the format described
+in the dma.txt file, using a two-cell specifier for each channel: the 1st
+specifies the channel group(DMAMUX) in which this request can be multiplexed,
+and the 2nd specifies the request source.
+
+Examples:
+
+sai2: sai at 40031000 {
+ compatible = "fsl,vf610-sai";
+ reg = <0x40031000 0x1000>;
+ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "sai";
+ clocks = <&clks VF610_CLK_SAI2>;
+ dma-names = "tx", "rx";
+ dmas = <&edma0 0 21>,
+ <&edma0 0 20>;
+ status = "disabled";
+};
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 9ae6f54..3d8a522 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -342,6 +342,16 @@ config K3_DMA
Support the DMA engine for Hisilicon K3 platform
devices.
+config FSL_EDMA
+ tristate "Freescale eDMA engine support"
+ depends on OF
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Support the Freescale eDMA engine with programmable channel
+ multiplexing capability for DMA request sources(slot).
+ This module can be found on Freescale Vybrid and LS-1 SoCs.
+
config DMA_ENGINE
bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 0a6f08e..e39c56b 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -43,3 +43,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_K3_DMA) += k3dma.o
+obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
new file mode 100644
index 0000000..1aa0eda
--- /dev/null
+++ b/drivers/dma/fsl-edma.c
@@ -0,0 +1,956 @@
+/*
+ * drivers/dma/fsl-edma.c
+ *
+ * Copyright 2013-2014 Freescale Semiconductor, Inc.
+ *
+ * Driver for the Freescale eDMA engine with flexible channel multiplexing
+ * capability for DMA request sources. The eDMA block can be found on some
+ * Vybrid and Layerscape SoCs.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+
+#include "virt-dma.h"
+
+#define EDMA_CR 0x00
+#define EDMA_ES 0x04
+#define EDMA_ERQ 0x0C
+#define EDMA_EEI 0x14
+#define EDMA_SERQ 0x1B
+#define EDMA_CERQ 0x1A
+#define EDMA_SEEI 0x19
+#define EDMA_CEEI 0x18
+#define EDMA_CINT 0x1F
+#define EDMA_CERR 0x1E
+#define EDMA_SSRT 0x1D
+#define EDMA_CDNE 0x1C
+#define EDMA_INTR 0x24
+#define EDMA_ERR 0x2C
+
+#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
+#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
+#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
+#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
+#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
+#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
+#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
+#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
+#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
+#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
+#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
+
+#define EDMA_CR_EDBG BIT(1)
+#define EDMA_CR_ERCA BIT(2)
+#define EDMA_CR_ERGA BIT(3)
+#define EDMA_CR_HOE BIT(4)
+#define EDMA_CR_HALT BIT(5)
+#define EDMA_CR_CLM BIT(6)
+#define EDMA_CR_EMLM BIT(7)
+#define EDMA_CR_ECX BIT(16)
+#define EDMA_CR_CX BIT(17)
+
+#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
+#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
+#define EDMA_CINT_CINT(x) ((x) & 0x1F)
+#define EDMA_CERR_CERR(x) ((x) & 0x1F)
+
+#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
+#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
+#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
+#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
+#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
+#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
+#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
+#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
+#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
+#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
+#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
+#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
+
+#define EDMA_TCD_SOFF_SOFF(x) (x)
+#define EDMA_TCD_NBYTES_NBYTES(x) (x)
+#define EDMA_TCD_SLAST_SLAST(x) (x)
+#define EDMA_TCD_DADDR_DADDR(x) (x)
+#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
+#define EDMA_TCD_DOFF_DOFF(x) (x)
+#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
+#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
+
+#define EDMA_TCD_CSR_START BIT(0)
+#define EDMA_TCD_CSR_INT_MAJOR BIT(1)
+#define EDMA_TCD_CSR_INT_HALF BIT(2)
+#define EDMA_TCD_CSR_D_REQ BIT(3)
+#define EDMA_TCD_CSR_E_SG BIT(4)
+#define EDMA_TCD_CSR_E_LINK BIT(5)
+#define EDMA_TCD_CSR_ACTIVE BIT(6)
+#define EDMA_TCD_CSR_DONE BIT(7)
+
+#define EDMAMUX_CHCFG_DIS 0x0
+#define EDMAMUX_CHCFG_ENBL 0x80
+#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
+
+#define DMAMUX_NR 2
+
+struct fsl_edma_hw_tcd {
+ u32 saddr;
+ u16 soff;
+ u16 attr;
+ u32 nbytes;
+ u32 slast;
+ u32 daddr;
+ u16 doff;
+ u16 citer;
+ u32 dlast_sga;
+ u16 csr;
+ u16 biter;
+};
+
+struct fsl_edma_sw_tcd {
+ dma_addr_t ptcd;
+ struct fsl_edma_hw_tcd *vtcd;
+};
+
+struct fsl_edma_slave_config {
+ enum dma_transfer_direction dir;
+ enum dma_slave_buswidth addr_width;
+ u32 dev_addr;
+ u32 burst;
+ u32 attr;
+};
+
+struct fsl_edma_chan {
+ struct virt_dma_chan vchan;
+ enum dma_status status;
+ struct fsl_edma_engine *edma;
+ struct fsl_edma_desc *edesc;
+ struct fsl_edma_slave_config fsc;
+ struct dma_pool *tcd_pool;
+};
+
+struct fsl_edma_desc {
+ struct virt_dma_desc vdesc;
+ struct fsl_edma_chan *echan;
+ bool iscyclic;
+ unsigned int n_tcds;
+ struct fsl_edma_sw_tcd tcd[];
+};
+
+struct fsl_edma_engine {
+ struct dma_device dma_dev;
+ void __iomem *membase;
+ void __iomem *muxbase[DMAMUX_NR];
+ struct clk *muxclk[DMAMUX_NR];
+ struct mutex fsl_edma_mutex;
+ u32 n_chans;
+ int txirq;
+ int errirq;
+ bool big_endian;
+ struct fsl_edma_chan chans[];
+};
+
+/*
+ * R/W functions for big- or little-endian registers
+ * the eDMA controller's endian is independent of the CPU core's endian.
+ */
+
+static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr)
+{
+ if (edma->big_endian)
+ return ioread16be(addr);
+ else
+ return ioread16(addr);
+}
+
+static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
+{
+ if (edma->big_endian)
+ return ioread32be(addr);
+ else
+ return ioread32(addr);
+}
+
+static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr)
+{
+ iowrite8(val, addr);
+}
+
+static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr)
+{
+ if (edma->big_endian)
+ iowrite16be(val, addr);
+ else
+ iowrite16(val, addr);
+}
+
+static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr)
+{
+ if (edma->big_endian)
+ iowrite32be(val, addr);
+ else
+ iowrite32(val, addr);
+}
+
+static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct fsl_edma_chan, vchan.chan);
+}
+
+static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct fsl_edma_desc, vdesc);
+}
+
+static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
+ edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
+}
+
+static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
+ edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
+}
+
+static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
+ unsigned int slot, bool enable)
+{
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+ void __iomem *muxaddr = fsl_chan->edma->muxbase[ch / DMAMUX_NR];
+ unsigned chans_per_mux, ch_off;
+
+ chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR;
+ ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
+
+ if (enable)
+ edma_writeb(fsl_chan->edma,
+ EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot),
+ muxaddr + ch_off);
+ else
+ edma_writeb(fsl_chan->edma, EDMAMUX_CHCFG_DIS, muxaddr + ch_off);
+}
+
+static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
+{
+ switch (addr_width) {
+ case 1:
+ return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT;
+ case 2:
+ return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT;
+ case 4:
+ return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
+ case 8:
+ return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT;
+ default:
+ return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
+ }
+}
+
+static void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
+{
+ struct fsl_edma_desc *fsl_desc;
+ int i;
+
+ fsl_desc = to_fsl_edma_desc(vdesc);
+ for (i = 0; i < fsl_desc->n_tcds; i++)
+ dma_pool_free(fsl_desc->echan->tcd_pool,
+ fsl_desc->tcd[i].vtcd,
+ fsl_desc->tcd[i].ptcd);
+ kfree(fsl_desc);
+}
+
+static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct dma_slave_config *cfg = (void *)arg;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ fsl_edma_disable_request(fsl_chan);
+ fsl_chan->edesc = NULL;
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ return 0;
+
+ case DMA_SLAVE_CONFIG:
+ fsl_chan->fsc.dir = cfg->direction;
+ if (cfg->direction == DMA_DEV_TO_MEM) {
+ fsl_chan->fsc.dev_addr = cfg->src_addr;
+ fsl_chan->fsc.addr_width = cfg->src_addr_width;
+ fsl_chan->fsc.burst = cfg->src_maxburst;
+ fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
+ } else if (cfg->direction == DMA_MEM_TO_DEV) {
+ fsl_chan->fsc.dev_addr = cfg->dst_addr;
+ fsl_chan->fsc.addr_width = cfg->dst_addr_width;
+ fsl_chan->fsc.burst = cfg->dst_maxburst;
+ fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+
+ case DMA_PAUSE:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ if (fsl_chan->edesc) {
+ fsl_edma_disable_request(fsl_chan);
+ fsl_chan->status = DMA_PAUSED;
+ }
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ return 0;
+
+ case DMA_RESUME:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ if (fsl_chan->edesc) {
+ fsl_edma_enable_request(fsl_chan);
+ fsl_chan->status = DMA_IN_PROGRESS;
+ }
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ return 0;
+
+ default:
+ return -ENXIO;
+ }
+}
+
+static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
+ struct virt_dma_desc *vdesc, bool in_progress)
+{
+ struct fsl_edma_desc *edesc = fsl_chan->edesc;
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+ enum dma_transfer_direction dir = fsl_chan->fsc.dir;
+ dma_addr_t cur_addr, dma_addr;
+ size_t len, size;
+ int i;
+
+ /* calculate the total size in this desc */
+ for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++)
+ len += edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes))
+ * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter));
+
+ if (!in_progress)
+ return len;
+
+ if (dir == DMA_MEM_TO_DEV)
+ cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
+ else
+ cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
+
+ /* figure out the finished and calculate the residue */
+ for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
+ size = edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes))
+ * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter));
+ if (dir == DMA_MEM_TO_DEV)
+ dma_addr = edma_readl(fsl_chan->edma,
+ &(edesc->tcd[i].vtcd->saddr));
+ else
+ dma_addr = edma_readl(fsl_chan->edma,
+ &(edesc->tcd[i].vtcd->daddr));
+
+ len -= size;
+ if (cur_addr > dma_addr && cur_addr < dma_addr + size) {
+ len += dma_addr + size - cur_addr;
+ break;
+ }
+ }
+
+ return len;
+}
+
+static enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+
+ status = dma_cookie_status(chan, cookie, txstate);
+ if (status == DMA_COMPLETE)
+ return status;
+
+ if (!txstate)
+ return fsl_chan->status;
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ vdesc = vchan_find_desc(&fsl_chan->vchan, cookie);
+ if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie)
+ txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true);
+ else if (vdesc)
+ txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false);
+ else
+ txstate->residue = 0;
+
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+ return fsl_chan->status;
+}
+
+static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan,
+ u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes,
+ u32 slast, u16 citer, u16 biter, u32 doff, u32 dlast_sga,
+ u16 csr)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ /*
+ * TCD parameters have been swapped in fill_tcd_params(),
+ * so just write them to registers in the cpu endian here
+ */
+ writew(0, addr + EDMA_TCD_CSR(ch));
+ writel(src, addr + EDMA_TCD_SADDR(ch));
+ writel(dst, addr + EDMA_TCD_DADDR(ch));
+ writew(attr, addr + EDMA_TCD_ATTR(ch));
+ writew(soff, addr + EDMA_TCD_SOFF(ch));
+ writel(nbytes, addr + EDMA_TCD_NBYTES(ch));
+ writel(slast, addr + EDMA_TCD_SLAST(ch));
+ writew(citer, addr + EDMA_TCD_CITER(ch));
+ writew(biter, addr + EDMA_TCD_BITER(ch));
+ writew(doff, addr + EDMA_TCD_DOFF(ch));
+ writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch));
+ writew(csr, addr + EDMA_TCD_CSR(ch));
+}
+
+static void fill_tcd_params(struct fsl_edma_engine *edma,
+ struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
+ u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
+ u16 biter, u16 doff, u32 dlast_sga, bool major_int,
+ bool disable_req, bool enable_sg)
+{
+ u16 csr = 0;
+
+ /*
+ * eDMA hardware SGs require the TCD parameters stored in memory
+ * the same endian as the eDMA module so that they can be loaded
+ * automatically by the engine
+ */
+ edma_writel(edma, src, &(tcd->saddr));
+ edma_writel(edma, dst, &(tcd->daddr));
+ edma_writew(edma, attr, &(tcd->attr));
+ edma_writew(edma, EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff));
+ edma_writel(edma, EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes));
+ edma_writel(edma, EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast));
+ edma_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer));
+ edma_writew(edma, EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff));
+ edma_writel(edma, EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga));
+ edma_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter));
+ if (major_int)
+ csr |= EDMA_TCD_CSR_INT_MAJOR;
+
+ if (disable_req)
+ csr |= EDMA_TCD_CSR_D_REQ;
+
+ if (enable_sg)
+ csr |= EDMA_TCD_CSR_E_SG;
+
+ edma_writew(edma, csr, &(tcd->csr));
+}
+
+static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
+ int sg_len)
+{
+ struct fsl_edma_desc *fsl_desc;
+ int i;
+
+ fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len,
+ GFP_NOWAIT);
+ if (!fsl_desc)
+ return NULL;
+
+ fsl_desc->echan = fsl_chan;
+ fsl_desc->n_tcds = sg_len;
+ for (i = 0; i < sg_len; i++) {
+ fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
+ GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
+ if (!fsl_desc->tcd[i].vtcd)
+ goto err;
+ }
+ return fsl_desc;
+
+err:
+ while (--i >= 0)
+ dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
+ fsl_desc->tcd[i].ptcd);
+ kfree(fsl_desc);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct fsl_edma_desc *fsl_desc;
+ dma_addr_t dma_buf_next;
+ int sg_len, i;
+ u32 src_addr, dst_addr, last_sg, nbytes;
+ u16 soff, doff, iter;
+
+ if (!is_slave_direction(fsl_chan->fsc.dir))
+ return NULL;
+
+ sg_len = buf_len / period_len;
+ fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
+ if (!fsl_desc)
+ return NULL;
+ fsl_desc->iscyclic = true;
+
+ dma_buf_next = dma_addr;
+ nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ iter = period_len / nbytes;
+ for (i = 0; i < sg_len; i++) {
+ if (dma_buf_next >= dma_addr + buf_len)
+ dma_buf_next = dma_addr;
+
+ /* get next sg's physical address */
+ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
+
+ if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ src_addr = dma_buf_next;
+ dst_addr = fsl_chan->fsc.dev_addr;
+ soff = fsl_chan->fsc.addr_width;
+ doff = 0;
+ } else {
+ src_addr = fsl_chan->fsc.dev_addr;
+ dst_addr = dma_buf_next;
+ soff = 0;
+ doff = fsl_chan->fsc.addr_width;
+ }
+
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr,
+ dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0,
+ iter, iter, doff, last_sg, true, false, true);
+ dma_buf_next += period_len;
+ }
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct fsl_edma_desc *fsl_desc;
+ struct scatterlist *sg;
+ u32 src_addr, dst_addr, last_sg, nbytes;
+ u16 soff, doff, iter;
+ int i;
+
+ if (!is_slave_direction(fsl_chan->fsc.dir))
+ return NULL;
+
+ fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
+ if (!fsl_desc)
+ return NULL;
+ fsl_desc->iscyclic = false;
+
+ nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ for_each_sg(sgl, sg, sg_len, i) {
+ /* get next sg's physical address */
+ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
+
+ if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ src_addr = sg_dma_address(sg);
+ dst_addr = fsl_chan->fsc.dev_addr;
+ soff = fsl_chan->fsc.addr_width;
+ doff = 0;
+ } else {
+ src_addr = fsl_chan->fsc.dev_addr;
+ dst_addr = sg_dma_address(sg);
+ soff = 0;
+ doff = fsl_chan->fsc.addr_width;
+ }
+
+ iter = sg_dma_len(sg) / nbytes;
+ if (i < sg_len - 1) {
+ last_sg = fsl_desc->tcd[(i + 1)].ptcd;
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd,
+ src_addr, dst_addr, fsl_chan->fsc.attr,
+ soff, nbytes, 0, iter, iter, doff, last_sg,
+ false, false, true);
+ } else {
+ last_sg = 0;
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd,
+ src_addr, dst_addr, fsl_chan->fsc.attr,
+ soff, nbytes, 0, iter, iter, doff, last_sg,
+ true, true, false);
+ }
+ }
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
+}
+
+static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
+{
+ struct fsl_edma_hw_tcd *tcd;
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&fsl_chan->vchan);
+ if (!vdesc)
+ return;
+ fsl_chan->edesc = to_fsl_edma_desc(vdesc);
+ tcd = fsl_chan->edesc->tcd[0].vtcd;
+ fsl_edma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->attr,
+ tcd->soff, tcd->nbytes, tcd->slast, tcd->citer,
+ tcd->biter, tcd->doff, tcd->dlast_sga, tcd->csr);
+ fsl_edma_enable_request(fsl_chan);
+ fsl_chan->status = DMA_IN_PROGRESS;
+}
+
+static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
+{
+ struct fsl_edma_engine *fsl_edma = dev_id;
+ unsigned int intr, ch;
+ void __iomem *base_addr;
+ struct fsl_edma_chan *fsl_chan;
+
+ base_addr = fsl_edma->membase;
+
+ intr = edma_readl(fsl_edma, base_addr + EDMA_INTR);
+ if (!intr)
+ return IRQ_NONE;
+
+ for (ch = 0; ch < fsl_edma->n_chans; ch++) {
+ if (intr & (0x1 << ch)) {
+ edma_writeb(fsl_edma, EDMA_CINT_CINT(ch),
+ base_addr + EDMA_CINT);
+
+ fsl_chan = &fsl_edma->chans[ch];
+
+ spin_lock(&fsl_chan->vchan.lock);
+ if (!fsl_chan->edesc->iscyclic) {
+ list_del(&fsl_chan->edesc->vdesc.node);
+ vchan_cookie_complete(&fsl_chan->edesc->vdesc);
+ fsl_chan->edesc = NULL;
+ fsl_chan->status = DMA_COMPLETE;
+ } else {
+ vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
+ }
+
+ if (!fsl_chan->edesc)
+ fsl_edma_xfer_desc(fsl_chan);
+
+ spin_unlock(&fsl_chan->vchan.lock);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
+{
+ struct fsl_edma_engine *fsl_edma = dev_id;
+ unsigned int err, ch;
+
+ err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR);
+ if (!err)
+ return IRQ_NONE;
+
+ for (ch = 0; ch < fsl_edma->n_chans; ch++) {
+ if (err & (0x1 << ch)) {
+ fsl_edma_disable_request(&fsl_edma->chans[ch]);
+ edma_writeb(fsl_edma, EDMA_CERR_CERR(ch),
+ fsl_edma->membase + EDMA_CERR);
+ fsl_edma->chans[ch].status = DMA_ERROR;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id)
+{
+ if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ return fsl_edma_err_handler(irq, dev_id);
+}
+
+static void fsl_edma_issue_pending(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+
+ if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
+ fsl_edma_xfer_desc(fsl_chan);
+
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+}
+
+static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
+ struct dma_chan *chan;
+
+ if (dma_spec->args_count != 2)
+ return NULL;
+
+ mutex_lock(&fsl_edma->fsl_edma_mutex);
+ list_for_each_entry(chan, &fsl_edma->dma_dev.channels, device_node) {
+ if (chan->client_count)
+ continue;
+ if ((chan->chan_id / DMAMUX_NR) == dma_spec->args[0]) {
+ chan = dma_get_slave_channel(chan);
+ if (chan) {
+ chan->device->privatecnt++;
+ fsl_edma_chan_mux(to_fsl_edma_chan(chan),
+ dma_spec->args[1], true);
+ mutex_unlock(&fsl_edma->fsl_edma_mutex);
+ return chan;
+ }
+ }
+ }
+ mutex_unlock(&fsl_edma->fsl_edma_mutex);
+ return NULL;
+}
+
+static int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+
+ fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
+ sizeof(struct fsl_edma_hw_tcd),
+ 32, 0);
+ return 0;
+}
+
+static void fsl_edma_free_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ fsl_edma_disable_request(fsl_chan);
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ fsl_chan->edesc = NULL;
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ dma_pool_destroy(fsl_chan->tcd_pool);
+ fsl_chan->tcd_pool = NULL;
+}
+
+static int
+fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
+{
+ int ret;
+
+ fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx");
+ if (fsl_edma->txirq < 0) {
+ dev_err(&pdev->dev, "Can't get edma-tx irq.\n");
+ return fsl_edma->txirq;
+ }
+
+ fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err");
+ if (fsl_edma->errirq < 0) {
+ dev_err(&pdev->dev, "Can't get edma-err irq.\n");
+ return fsl_edma->errirq;
+ }
+
+ if (fsl_edma->txirq == fsl_edma->errirq) {
+ ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
+ fsl_edma_irq_handler, 0, "eDMA", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA IRQ.\n");
+ return ret;
+ }
+ } else {
+ ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
+ fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, fsl_edma->errirq,
+ fsl_edma_err_handler, 0, "eDMA err", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int fsl_edma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_edma_engine *fsl_edma;
+ struct fsl_edma_chan *fsl_chan;
+ struct resource *res;
+ int len, chans;
+ int ret, i;
+
+ ret = of_property_read_u32(np, "dma-channels", &chans);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get dma-channels.\n");
+ return ret;
+ }
+
+ len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans;
+ fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_edma)
+ return -ENOMEM;
+
+ fsl_edma->n_chans = chans;
+ mutex_init(&fsl_edma->fsl_edma_mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_edma->membase))
+ return PTR_ERR(fsl_edma->membase);
+
+ for (i = 0; i < DMAMUX_NR; i++) {
+ char clkname[32];
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
+ fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_edma->muxbase[i]))
+ return PTR_ERR(fsl_edma->muxbase[i]);
+
+ sprintf(clkname, "dmamux%d", i);
+ fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
+ if (IS_ERR(fsl_edma->muxclk[i])) {
+ dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
+ return PTR_ERR(fsl_edma->muxclk[i]);
+ }
+
+ ret = clk_prepare_enable(fsl_edma->muxclk[i]);
+ if (ret) {
+ dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
+ return ret;
+ }
+
+ }
+
+ ret = fsl_edma_irq_init(pdev, fsl_edma);
+ if (ret)
+ return ret;
+
+ fsl_edma->big_endian = of_property_read_bool(np, "big-endian");
+
+ INIT_LIST_HEAD(&fsl_edma->dma_dev.channels);
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
+
+ fsl_chan->edma = fsl_edma;
+
+ fsl_chan->vchan.desc_free = fsl_edma_free_desc;
+ vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
+
+ edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ }
+
+ dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask);
+ dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask);
+ dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask);
+
+ fsl_edma->dma_dev.dev = &pdev->dev;
+ fsl_edma->dma_dev.device_alloc_chan_resources
+ = fsl_edma_alloc_chan_resources;
+ fsl_edma->dma_dev.device_free_chan_resources
+ = fsl_edma_free_chan_resources;
+ fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
+ fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
+ fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic;
+ fsl_edma->dma_dev.device_control = fsl_edma_control;
+ fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
+
+ platform_set_drvdata(pdev, fsl_edma);
+
+ ret = dma_async_device_register(&fsl_edma->dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register Freescale eDMA engine.\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma.\n");
+ dma_async_device_unregister(&fsl_edma->dma_dev);
+ return ret;
+ }
+
+ /* enable round robin arbitration */
+ edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase + EDMA_CR);
+
+ return 0;
+}
+
+static int fsl_edma_remove(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
+ int i;
+
+ of_dma_controller_free(np);
+ dma_async_device_unregister(&fsl_edma->dma_dev);
+
+ for (i = 0; i < DMAMUX_NR; i++)
+ clk_disable_unprepare(fsl_edma->muxclk[i]);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_edma_dt_ids[] = {
+ { .compatible = "fsl,vf610-edma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
+
+static struct platform_driver fsl_edma_driver = {
+ .driver = {
+ .name = "fsl-edma",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_edma_dt_ids,
+ },
+ .probe = fsl_edma_probe,
+ .remove = fsl_edma_remove,
+};
+
+module_platform_driver(fsl_edma_driver);
+
+MODULE_ALIAS("platform:fsl-edma");
+MODULE_DESCRIPTION("Freescale eDMA engine driver");
+MODULE_LICENSE("GPL v2");
--
1.8.0
^ 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